Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copy to .env and fill in. Rook loads .env automatically on startup.

# ChatBotKit API token required.
# ChatBotKit API token - required.
CHATBOTKIT_API_SECRET=
3 changes: 3 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ on:
push:
tags:
- 'v*'
# Dispatched by tag-release.yaml at the new tag ref. Needed because tags
# pushed with GITHUB_TOKEN do not trigger the push-tags event above.
workflow_dispatch:

permissions:
contents: write
Expand Down
66 changes: 66 additions & 0 deletions .github/workflows/tag-release.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Auto-tag when merging to main.
#
# Reads the version from the VERSION file and, if the matching tag does not
# already exist, creates and pushes it, then dispatches the Release workflow
# (which builds and publishes the binaries). The dispatch is required because
# tags pushed with GITHUB_TOKEN do not trigger other workflows.
name: Tag Release

on:
push:
branches:
- main

jobs:
tag:
runs-on: ubuntu-latest
permissions:
contents: write
actions: write
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Get version from VERSION file
id: version
run: |
if [ -f VERSION ]; then
VERSION=$(cat VERSION)
else
VERSION="0.0.0"
fi
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Version: $VERSION"

- name: Check if tag exists
id: check_tag
run: |
if git rev-parse "v${{ steps.version.outputs.version }}" >/dev/null 2>&1; then
echo "exists=true" >> $GITHUB_OUTPUT
echo "Tag v${{ steps.version.outputs.version }} already exists"
else
echo "exists=false" >> $GITHUB_OUTPUT
echo "Tag v${{ steps.version.outputs.version }} does not exist"
fi

- name: Create and push tag
if: steps.check_tag.outputs.exists == 'false'
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag -a "v${{ steps.version.outputs.version }}" -m "Release v${{ steps.version.outputs.version }}"
git push origin "v${{ steps.version.outputs.version }}"

- name: Trigger release workflow
if: steps.check_tag.outputs.exists == 'false'
uses: actions/github-script@v7
with:
script: |
await github.rest.actions.createWorkflowDispatch({
owner: context.repo.owner,
repo: context.repo.repo,
workflow_id: 'release.yaml',
ref: 'v${{ steps.version.outputs.version }}'
})
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# Release artifacts
/dist/

# Go workspace local development only
# Go workspace - local development only
go.work
go.work.sum

Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ GOSDK ?= ../go-sdk

workspace:
go work init . $(GOSDK)
@echo "go.work created local builds now use $(GOSDK)"
@echo "go.work created - local builds now use $(GOSDK)"

build:
@echo "Building $(CMD) ($(VERSION))..."
Expand Down
2 changes: 1 addition & 1 deletion NOTICE.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Rook bundles third-party content under the terms below. This file is provided to
satisfy the attribution requirements of those licenses.

## Embedded skill library `skills/`
## Embedded skill library - `skills/`

The contents of the [`skills/`](skills/) directory are sourced from the
**claude-bughunter** project by Sachin Sharma:
Expand Down
110 changes: 92 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
**Rook** is a standalone, autonomous security agent for vulnerability research,
bug hunting and source-code auditing. It is a single Go executable built on the
[ChatBotKit Go SDK](https://github.com/chatbotkit/go-sdk), with a library of
security skills embedded directly into the binary no external files, no setup
security skills embedded directly into the binary - no external files, no setup
beyond an API key.

Give Rook a target and a scope, and it works through the problem the way a
Expand All @@ -16,11 +16,48 @@ written report.
> against systems, code and services you own or are explicitly authorized to
> test. Always pass an explicit `--scope`.

## Why Rook?

Security work happens in awkward places - a hardened bastion, an air-gapped
network, a throwaway cloud VM, a CI runner, someone else's laptop during an
engagement. Rook is built for exactly those:

- **One single executable.** Everything - the agent loop, the tools, and the
entire skill library - is compiled into one binary via Go's `embed`. There is
no runtime to install, no interpreter, no `node_modules`, no virtualenv, no
config files to ship alongside it. Download one file, `chmod +x`, run.
- **Portable everywhere.** Statically linked (`CGO_ENABLED=0`) and
cross-compiled for Linux, macOS and Windows on both amd64 and arm64. The same
tool drops onto an Apple-silicon laptop, an x86 server, or an ARM box with no
changes. Nothing to match against the host's libraries or OS version.
- **Nothing to fetch at runtime.** Because the skills are baked in, Rook works
in locked-down or offline environments where you can't `pip install` or pull
containers. Its only external dependency is the ChatBotKit API (and your key).
- **The hard parts run as a service.** This is the real reason Rook feels so
light. The AI agent harness - model orchestration, the reasoning and
tool-execution loop, skill handling, scaling and reliability - runs as a
managed service on ChatBotKit, built and maintained by a dedicated team of
engineers who do only this. The binary doesn't reimplement any of that
complexity; it embeds the skills and streams the conversation. So the agent
itself stays small and focused on the task at hand, and you inherit harness
improvements without shipping a new build.
- **Trivial to distribute and audit.** A single artifact with a published
checksum is easy to vet, copy onto a target box, version-pin, and remove
cleanly afterwards - important when you're operating inside someone else's
scope.
- **Purpose-built, not a general chatbot.** Rook ships as a focused
vulnerability-research and bug-hunting agent: it knows the methodology, the
bug classes, and the reporting discipline out of the box, and stays within
the authorization boundary you give it.

In short: the value isn't just "an AI security agent" - it's an AI security
agent you can carry anywhere as **one file** and run with **zero setup**.

## Features

- **Single self-contained binary.** The skill library is compiled into the
executable via Go's `embed`, so it ships and runs as one file.
- **Autonomous agent loop.** Built on the Go SDK's `agent.ExecuteWithTools`
- **Autonomous agent loop.** Built on the Go SDK's `agent.ExecuteWithTools` -
the agent plans, acts, tracks progress and exits on its own, bounded by
`--max-iterations`.
- **Built-in tools.** File read/write/edit and sandboxed shell execution via
Expand Down Expand Up @@ -50,6 +87,43 @@ Or clone and build with the provided `Makefile`:
make build # → ./rook
```

## Authentication

Rook talks to the ChatBotKit API, so it needs an API token supplied via
`CHATBOTKIT_API_SECRET`.

1. **Create a ChatBotKit account** at [chatbotkit.com](https://chatbotkit.com)
or [console.cbk.ai](https://console.cbk.ai).
2. **Create an API token** from the Tokens page
([chatbotkit.com/tokens](https://chatbotkit.com/tokens)) and set it as
`CHATBOTKIT_API_SECRET` (export it, or put it in a `.env` file).

### Recommended: run under a sub-account

For better **isolation, cost control and observability**, we suggest running
Rook under a dedicated **sub-account** rather than your main account - each
engagement, tool or user then gets its own usage, billing and logs. For a
sub-account that is fully dedicated to Rook, a **standard token is enough**.

### Recommended: use a scoped token

We also recommend a **scoped token**, which limits the token to specific
ChatBotKit API routes (principle of least privilege), so a leaked key can't
touch the rest of your account. This matters less for a fully dedicated
sub-account, but it is good practice everywhere.

Rook runs **statelessly**, so it only needs the stateless completion route.
When creating the token, set its `allowedRoutes` to:

```yaml
allowedRoutes:
- conversation/complete
```

Route patterns omit the `/v1/` prefix. See
[How to Create Scoped API Tokens](https://chatbotkit.com/tutorials/how-to-create-scoped-api-tokens-for-restricted-access)
for the full guide.

## Usage

```bash
Expand All @@ -74,41 +148,41 @@ Rook loads a `.env` file automatically if present (see `.env.example`).
| ------------------ | --------------- | --------------------------------------------- |
| `--model` | `qwen-3.6-plus` | Model the agent reasons with |
| `--max-iterations` | `40` | Maximum agent iterations before a forced stop |
| `--scope` | | Authorization boundary (hosts, repos, paths) |
| `--scope-file` | | Read the authorization scope from a file |
| `--scope` | - | Authorization boundary (hosts, repos, paths) |
| `--scope-file` | - | Read the authorization scope from a file |
| `-v`, `--verbose` | `false` | Stream the agent's reasoning tokens to stdout |
| `-V`, `--version` | | Print version and exit |
| `-V`, `--version` | - | Print version and exit |

The agent's findings stream to **stderr**; with `--verbose`, reasoning tokens
stream to **stdout**. The final report is delivered as the agent's response
stream to **stdout**. The final report is delivered as the agent's response -
Rook does not write files on its own. If you want the report (or any other
artifact) saved to disk, ask for it in the task and the agent will use its
`write` tool.

## Embedded Skills

Rook ships with **51 security skills** each a `SKILL.md` playbook under
Rook ships with **51 security skills** - each a `SKILL.md` playbook under
[`skills/`](skills/), embedded into the binary at build time and offered to the
agent as it works. They cover, roughly:

- **Methodology & mindset** `bug-bounty`, `bb-methodology`, `redteam-mindset`,
- **Methodology & mindset** - `bug-bounty`, `bb-methodology`, `redteam-mindset`,
`bb-local-toolkit`, `hunt-dispatch`.
- **Web/API vulnerability hunting** (24 `hunt-*` classes + `security-arsenal`)
- **Web/API vulnerability hunting** (24 `hunt-*` classes + `security-arsenal`) -
IDOR, SQLi, XSS, SSRF, RCE, SSTI, XXE, CSRF, OAuth, SAML, GraphQL, auth/MFA
bypass, ATO, business logic, cache poisoning, HTTP smuggling, file upload,
API misconfig, race conditions, and more.
- **Enterprise & infrastructure attack chains** `m365-entra-attack`,
- **Enterprise & infrastructure attack chains** - `m365-entra-attack`,
`okta-attack`, `cloud-iam-deep`, `vmware-vcenter-attack`,
`enterprise-vpn-attack`, `hunt-sharepoint`, `hunt-aspnet`, `hunt-ntlm-info`,
`apk-redteam-pipeline`, `supply-chain-attack-recon`.
- **Recon & OSINT** `web2-recon`, `offensive-osint`, `osint-methodology`,
- **Recon & OSINT** - `web2-recon`, `offensive-osint`, `osint-methodology`,
`hunt-subdomain`.
- **Web3** `web3-audit`, `meme-coin-audit`.
- **Triage, reporting & hygiene** `triage-validation`, `bugcrowd-reporting`,
- **Web3** - `web3-audit`, `meme-coin-audit`.
- **Triage, reporting & hygiene** - `triage-validation`, `bugcrowd-reporting`,
`report-writing`, `redteam-report-template`, `evidence-hygiene`,
`mid-engagement-ir-detection`.

These skills are sourced from the **claude-bughunter** project see
These skills are sourced from the **claude-bughunter** project - see
[Credits](#credits).

### Adding a skill
Expand All @@ -126,7 +200,7 @@ description: One sentence the model uses to decide when to apply this skill.
Step-by-step guidance...
```

Rebuild the binary the new skill is picked up automatically by the `embed`
Rebuild the binary - the new skill is picked up automatically by the `embed`
directive. No registration code required.

## How it works
Expand All @@ -140,8 +214,8 @@ embed.go //go:embed skills → the embedded skill library
skills/ SKILL.md playbooks compiled into the binary
```

The default model and the agent's system prompt (backstory) live in one place
[`internal/config/config.go`](internal/config/config.go) so they can be tuned
The default model and the agent's system prompt (backstory) live in one place -
[`internal/config/config.go`](internal/config/config.go) - so they can be tuned
without touching the CLI or the agent loop.

At startup Rook loads the embedded skills with `agent.LoadSkillsFromFS`,
Expand Down Expand Up @@ -192,5 +266,5 @@ author and the bug-bounty community whose disclosed reports informed them.

## License

Rook itself is MIT licensed see [LICENSE](LICENSE). Bundled third-party
Rook itself is MIT licensed - see [LICENSE](LICENSE). Bundled third-party
content retains its original license; see [NOTICE.md](NOTICE.md).
4 changes: 2 additions & 2 deletions RELEASES.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ multi-platform binaries and publishes them as a GitHub Release.
### go-sdk resolution

The committed `go.mod` pins a **tagged release** of the Go SDK (e.g.
`github.com/chatbotkit/go-sdk v0.1.0`), so every build clean clone, CI,
release, and `go install` uses exactly that version. Builds are reproducible;
`github.com/chatbotkit/go-sdk v0.1.0`), so every build - clean clone, CI,
release, and `go install` - uses exactly that version. Builds are reproducible;
no floating fetch step is involved.

For development against a local checkout of the SDK, a **gitignored `go.work`**
Expand Down
1 change: 1 addition & 0 deletions VERSION
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0.1.0
2 changes: 1 addition & 1 deletion cmd/rook/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func main() {
showVersion := flags.BoolP("version", "V", false, "print version and exit")

flags.Usage = func() {
fmt.Fprintf(os.Stderr, "rook autonomous security research agent\n\n")
fmt.Fprintf(os.Stderr, "rook - autonomous security research agent\n\n")
fmt.Fprintf(os.Stderr, "Usage:\n rook [flags] <task>\n rook version\n\nFlags:\n")
flags.PrintDefaults()
}
Expand Down
2 changes: 1 addition & 1 deletion internal/agent/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func Run(ctx context.Context, cfg Config) (int, error) {

fmt.Fprintf(os.Stderr, "Loaded %d embedded skill(s):\n", len(skills))
for _, s := range skills {
fmt.Fprintf(os.Stderr, " • %s %s\n", s.Name, s.Description)
fmt.Fprintf(os.Stderr, " • %s - %s\n", s.Name, s.Description)
}
fmt.Fprintln(os.Stderr)

Expand Down
Loading