AI autonomous penetration testing system (Go backend + Next.js frontend)
🌐 Online Demo: https://artex-demo.vercel.app/
See Online Demo for complete interaction.
| Dashboard (Overview/Token Consumption/Activity Flow) | Task List |
|---|---|
| Task · Execution Process (Session/Tool Call) | Explore Links |
|---|---|
| Discovery | Assets |
|---|---|
| Asset Overlay (Force-Directed Layout · Measured Highlighting · Node Collapse and Expand) |
|---|
| Traffic recording | Human-in-the-loop conversation |
|---|---|
| Agent Management | LLM Configuration |
|---|---|
| Interception Approval | Backend Log |
|---|---|
Global "Approval Records", "Interception Approval" within tasks, and approval cards in conversations all support expanding to view details. Display structure reference AegisHook’s approval details component, following the components and themes of ARTEX:
Supports direct synchronization of asset data from ScopeSentry to avoid repeated collection:
- Fill in the ScopeSentry address and API Key on the "Asset Synchronization" page to access the data source;
- Select the target and asset type to be synchronized (domain name / subdomain / IP / port / site / endpoint...) according to the project or task dimension;
- One-click import and merge according to the company's asset range, directly enter the ARTEX asset map for agent exploration.
Depends on database PostgreSQL; exploration requires configuring LLM (
ANTHROPIC_API_KEYorOPENAI_API_KEY, which can also be configured in the UI).
git clone https://github.com/Autumn-27/ARTEX.git
cd ARTEX
./install.shThe script detects/automatically installs Docker, then lets you choose ① All Docker or ② Build and run locally:
- ① All Docker: Fill in a Postgres password (you can press Enter to make it random) → automatically write
.env→docker compose up -d. - ② Local operation: Select database (connect to existing one/use Docker to start one) → Generate
config.json→gocompile embedded single binary → Start.
After installation, open http://localhost:8787 (visit /setup the first time to set the administrator password).
git clone https://github.com/Autumn-27/ARTEX.git
cd ARTEX
cp .env.example .env # Fill in POSTGRES_PASSWORD, optional ANTHROPIC_API_KEY
docker compose up -d # Pull autumn27/artex image + postgres
# → http://localhost:8787The image already contains commonly used tools (ripgrep/curl/vim/npm/nmap…); ./skills and ./data are persisted by bind mounting.
Remote MCP can select http (Streamable HTTP) or sse (legacy SSE) in the system settings.
Older versions of SSE services usually use GET /sse to establish event streams, and then return them through the service
/message?sessionId=... receives JSON-RPC requests; when configuring, fill in the URL as /sse and click the request header
Authorization=Bearer <token> Fill in.
Go to Releases to download the zip of the corresponding platform. After decompressio
8BD0
n, you will get artex + start.sh (start.bat for Windows) + skills/ + config.example.json:
cp config.example.json config.json # Fill in the database connection
./start.sh # → http://localhost:8787Please use
start.sh/start.batto start instead of running./artexdirectly. It is a daemon script: after the program exits, press the exit code to decide whether to restart it. The [One-click update] on the ** page (#method one page one-click update recommendation) relies on it to complete the change**. When running./artexdirectly, it will not be pulled up after updating. Backend resident:nohup ./start.sh >artex.log 2>&1 &.
# 1) Front-end static export
cd web && npm ci && npm run build:static && cd ..
# 2) Copy into the embedded directory
cp -r web/out server/webui/dist
# 3) Compile (-tags embedui only embeds the front end)
CGO_ENABLED=0 go build -tags embedui -o artex ./cmd/artex
./start.shbuild.sh will first build and embed the front end, then use Go linker to remove debugging information and compress the release file into a zip. Release mode generates zip packages for Linux amd64/arm64, macOS amd64/arm64 and Windows amd64 by default:
./build.sh --release
# Product: dist/artex-0.3.3-*.zipUPX self-extracting binaries may be incompatible with some Linux kernels, virtualization environments, or security policies and therefore are not enabled by default. You can use ARTEX_TARGETS to customize the target; when confirming that the target operating environment is compatible, you can explicitly pass in --upx to further shrink the binary:
ARTEX_TARGETS=linux/amd64,windows/amd64 ./build.sh --release
./build.sh --target linux/amd64 --upxUpgrade only changes the program and leaves the data unchanged: Postgres data volumes
pgdata,./data(jwt.key / SQLite, etc.),./skillswill be retained. Database migration does not need to be performed manually -artexwill rerunschema.sql(includingADD COLUMN/CREATE INDEX IF NOT EXISTS) idempotently every time it is started, that is, "restart and migrate". It is still recommended to back up./dataand the database before upgrading.
In the Version and Updates card of the System Configuration page (sidebar "System Configuration" → /system/settings), you can directly check and install the new version without logging in to the server.
After clicking "Update": download the release package for the current platform → verify it against the release SHA256SUMS → smoke-test the new binary with -h → stage it as artex.new → exit so start.sh / start.bat can restart the program and complete the replacement. The page waits for the new version and refreshes automatically.
- Failure will not leave bad programs: If the verification or smoke fails, discard the temporary file and continue running the current version; if the new version fails to start three times in a row, it will automatically roll back to
artex.old(the failed one is reserved asartex.failedfor troubleshooting). - Rollback at any time: The previous version remains as
artex.old, with "Rollback to previous version" on the card. Note that the database structure will not be rolled back. - Update will interrupt running tasks - Update will restart, please do it when you are free.
- Development builds will not be updated: Disabled when the version number is
devorgit describewith a suffix to prevent the official version from overwriting the local debugged binary. - Only change the program under Docker, not the image: Tool chains such as playwright / nmap in the image will not be upgraded, and
docker compose up -dwill return to the version that comes with the image after rebuilding the container. To upgrade the image together, please usedocker compose pull artex && docker compose up -d artex. - When accessing GitHub requires a proxy, just configure the global proxy on the same page, and the updated link will use it. Updates are only downloaded from GitHub domains and force HTTPS.
cd ARTEX
./update.shThe script first optionally runs git pull for the latest code, then lets you choose ① Docker update or ② Local build update (corresponding to install.sh):
- ① Docker: You can specify the target image tag (press Enter to use
ARTEX_TAGof.env, the default islatest) →docker compose pull→docker compose up -d(replace the new image and restart to automatically migrate). - ② Local: Rebuild the front-end static product → Recompile
./artex(restart the process after completion to take effect).
cd ARTEX
git pull # Update compose/script (optional)
# Specify version: set ARTEX_TAG=v0.2.0 in .env; if not set, use latest
docker compose pull artex
docker compose up -d artex # Replace with new image and restart → Automatically migrate schema
docker image prune -f # Clean up old images (optional)Go to Releases to download the new version zip, stop the old process, overwrite artex and skills/ (keep your config.json and data/), and restart:
cp -r <decompression directory>/skills ./ && cp <decompression directory>/artex ./
./start.shgit pull
cd web && npm ci && npm run build:static && cd ..
cp -r web/out server/webui/dist
CGO_ENABLED=0 go build -tags embedui -o artex ./cmd/artex
# Restart ./start.shDatabase (config.json, or override with environment variable ARTEX_PG_DSN):
{
"database": {
"host": "127.0.0.1", "port": 5432,
"user": "artex", "password": "yourpass",
"dbname": "artex", "sslmode": "disable"
}
}LLM: export ANTHROPIC_API_KEY=sk-... (or OPENAI_API_KEY), which can also be filled in on the "LLM Configuration" page of the UI.
Optional: ARTEX_LLM_PROVIDER / ARTEX_LLM_MODEL / ARTEX_LLM_BASE_URL / ARTEX_LLM_PROXY.
Concurrency: The number of work agents for each task is configured in "System Settings" (default 3).
Commonly used parameters: ./start.sh -addr :8787 -proxy :8788 (-addr front-end + API, -proxy traffic recording proxy). The startup script will transparently pass the parameters to artex as they are.
Both the front-end and API/SSE are provided by the same back-end port (default :8787). The real-time activity stream defaults to the same-origin address, so there is no need to configure NEXT_PUBLIC_SSE_BASE**. The public network is only open to 443. Just leave 8787 on the intranet.
SSE is a long connection + continuous push. The anti-generation must turn off the buffer, otherwise the browser can connect but cannot receive events (shown as the activity stream keeps spinning). Nginx example:
server {
listen 443 ssl;
server_name your.domain.com;
# ssl_certificate / ssl_certificate_key ...
location / {
proxy_pass http://127.0.0.1:8787;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
# SSE key items: off buffering, long timeout, HTTP/1.1
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 3600s;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}Only when SSE needs to go to a source different from the page (such as an independent subdomain), set
NEXT_PUBLIC_SSE_BASEduring the build period (this variable is solidified into the static package duringnext build, and is invalid when the container is running).
The "Retest" tab of the task details allows you to select the vulnerabilities of this task in pages, view previous conclusions and evidence, and manually initiate a retest. After startup, the current tab will be retained and the circle icon and "under retest" will be displayed; the vulnerability status will be updated simultaneously after confirmation of repair.
Click "Retest" in the operation area of each row of the vulnerability list, or click "Initiate retest" in the "Vulnerability Retest" area of the vulnerability details, fill in the optional repair version, test conditions or restrictions, the system will create an independent retest Agent session, and retain the current page after startup. The tiling of the list, grouping by tasks, and asset view all support this entry; when the retest is running, the circle icon and "Retesting" are displayed. When you need to view it, click to enter the corresponding session, and "Retest" is restored after the end. Retesting does not require restarting the original scanning task. The conclusions are divided into "still reproducible", "fixed" and "unconfirmable". Each conclusion, evidence and session link are saved in the vulnerability details.
When the new backend is launched for the first time, the editable "retester" Agent will be preset. Prompt words, LLM, operating budget and tools can be configured in Agent management. By default, the bound LLM is used. If it is not bound, the global activation configuration is used. When the retest session is successfully completed and the conclusion is "Fixed", the system automatically changes the vulnerability processing status to "Fixed"; the execution, failure, stop, or other conclusions remain in their original status. Original evidence and reports are always retained. You can also manually select "Repaired" in the status drop-down menu. Existing sessions can be reused when the same vulnerability is being retested, and can be reinitiated after stopping, failing or restarting the service.
The history of this version can be viewed through vulnerability details and sessions. It has not yet been included in the vulnerability report export or task archive package, nor is it automatically associated with traffic packages. Demonstration mode only generates explicitly annotated simulation records and does not request real targets.
./dev.sh # Backend(:8787) + Traffic proxy(:8788) + Frontend next dev(:5173) → http://localhost:5173- Backend:
go run ./cmd/artex(without-tags embedui, the front end will not be embedded) - Front-end:
cd web && npm run dev(/apiis reversed to the back-end, with hot update) - Test:
go test ./... - Mock preview (no backend):
cd web && NEXT_PUBLIC_MOCK=1 npm run dev
ARTEX is a set of LLM multi-agent driven autonomous penetration system: Go single backend (embedded Next.js front-end) + PostgreSQL, agent capabilities are provided by norma SDK (agentcore / tool / permission / harness / memory / transcript). The core is the dual graph architecture and the two autonomous mechanisms surrounding it: process-level information exchange between workers and planner's multi-round sharing of todolist stable attack links.
flowchart TB
subgraph FE["Front-end Next.js (go:embed embedded single binary)"]
UI["Dashboard · Tasks · Assets · Overlays · Traffic · Workspace · System Configuration"]
end
subgraph SRV["server(Go net/http)"]
API["REST /api/* JWT Authentication SSE"]
ENG["engine scheduling loop"]
MGR["Manager Task/Engine/store Lifecycle"]
end
subgraph AG["agent(norma SDK)"]
GO["goals Goal decomposition + extraction range"]
PL["planner Planner (the only intention generator)"]
WK["worker executor×N"]
MA["mainagent: human in the loop"]
end
subgraph DB["PostgreSQL"]
AGRAPH["Asset Map assets / companies / task_scope"]
EGRAPH["Exploration graph exploration_nodes / anchors / activity"]
end
subgraph SUB["Support Subsystem"]
PROXY["Traffic recording proxy MITM + CA traces"]
GUARD["guard / intercept Tool Approval Gate"]
ENR["enrich DNS / HTTP asynchronous completion"]
EXT["MCP · skills · memory · report"]
end
UI -->|HTTP| API
API --> MGR --> ENG
ENG --> PL
ENG --> WK
API --> MA
API --> GO
PL --> DB
WK --> DB
MA --> DB
GO --> DB
WK -->|"Bash / HTTP full traces"| PROXY
WK --> GUARD
WK --> ENR
PL -.-> EXT
WK -.-> EXT
MA -.-> EXT
| Layer | Responsibilities |
|---|---|
| Front-end | Next.js static export, go:embed embedded into single binary; visual tasks/assets/exploration links/overlays, human-in-the-loop dialogue |
| server | net/http routing + JWT authentication + SSE; Manager managed task, engine, DB store life cycle |
| engine | One plannerLoop + N worker goroutines per task; intent to receive, timeout/pause/drain |
| agent | goals/planner/worker/mainagent, ToolSet exposes dual graphs as LLM tools |
| db | Postgres implementation of dual graphs (pgx); schema comes with go:embed to create idempotent tables every time it is started |
| Support | Recorded MITM agent, approval gate, asynchronous completion, MCP/skill/memory/report |
The system splits "What is the target" and "To what extent was measured" into two independent pictures that are connected through anchor points:
- Asset Graph (global sharing): The same asset truth value library across tasks. The node is
root_domain / subdomain / ip / service / app / endpointand belongs to the company; the parent-child relationship and deduplication key of domain name → subdomain → service → endpoint are all calculated by the program, and the agent only submits the original information. - Exploration Graph (each task is independent): The "thinking and advancement" process of a task. The nodes are
goal (goal) / intent (intention) / fact (fact) / finding (loophole) / hint (hint), which are connected byspawns / derived_from / yields / provesand other edges to form a blood chain, answering "which direction is derived from which facts and what is produced". - The two images are connected by anchor points:
exploration_anchors(node_id, asset_id)anchors the intent/fact/vulnerability to a specific asset - so you can not only see which assets it targets from the "exploration direction", but also check back from "a certain asset" which intent it has been tested by in this task and what facts it has obtained. This also supports Asset Test Coverage and Asset Coverage (Assets in Range + Tested Highlighting).
flowchart LR
subgraph EG["Exploration graph (independent for each task·Propulsion chain)"]
direction TB
G["goal goal"]
I1["intent intention A"]
F1["fact fact"]
I2["intent intention B"]
FD["finding vulnerability"]
G -->|spawns| I1
I1 -->|yields| F1
F1 -->|derived_from| I2
I2 -->|proves| FD
end
subgraph AG["Asset graph (global sharing · truth library)"]
direction TB
RD["root_domain"]
SD["subdomain"]
SV["service"]
EP["endpoint"]
RD --> SD --> SV --> EP
end
I1 -. anchor .-> SD
F1 -. anchor .-> SV
I2 -. anchor .-> EP
FD -. anchor .-> EP
Division of work: planner reads the exploration map situation, determines the target, and only sends intent to the frontier when there is a new direction that is not covered; worker receives an intention, executes it with real tools, writes new assets/facts/vulnerabilities back to the two maps and then stops. The asset graph is the shared facts, and the exploration graph is the advancement chain of each task.
The engine is an event-driven closed loop: the planner is awakened as soon as the graph changes, the planner sends the intention, the worker executes the intention and writes it back, and the writing back triggers the next round - until the goal is proven (prove_goal).
sequenceDiagram
autonumber
participant EV as graph change debounce
participant P as planner
participant FR as frontier intent queue
participant W as worker
participant PX as record agent
participant DB as double picture + activity
EV-->>P: Wake up
P->>DB: Read situation (graph_overview prefetch + coverage/scope)
P->>FR: Send 0..N intents (with asset_ids)
Note over P,FR: Most Awakenings 0 - ends without new direction
W->>FR: claimNext Claim an intention
W->>DB: Get the original asset of intent asset_ids as initial information
W->>PX: Real Tool Execution (Kali/Bash/HTTP)
PX-->>W: Response (full trace + CA verification)
W->>DB: write back fact / asset / finding + each step activity
DB-->>EV: Image change
EV-->>P: Wake up again (closed loop)
During an in-depth exploration, many valuable observations (a certain error, a certain response, a certain hidden parameter) appeared in the execution process of a worker, but they may not be written as formal facts. In order to avoid duplication of work and allow workers on the link to stand on each other's shoulders, workers have the ability to cross work retrieval processes:
search_all_worker_traces(q): Search by keyword in the execution process of other work in this task (automatically exclude the step of your own intention), the hit item hasintent_id;list_worker_traces/get_worker_trace(intent_id, step_ids=[…]): First check which work has run, and then get the complete content of specific steps of a certain work to exchange details.
In this way, even if there is no corresponding fact on the exploration graph, subsequent workers can reuse the observations of others in the process - Information flows between workers at the granularity of "execution process", and the boundaries remain unchanged (each worker still only does the intention he received).
flowchart LR
WA["worker A (Intent #12)"] -->|"Every step activity"| ACT[("Exploration diagram · activity process library")]
WB["worker B (Intent #34)"] -->|"per step activity"| ACT
WC["worker C (Intent #56)"] ==>|"1) search_all_worker_traces(q)"| ACT
ACT ==>|"2) Steps to hit A/B (exclude yourself)"| WC
WC ==>|"3) get_worker_trace(id, step_ids)"| ACT
ACT ==>|"4) Return to complete process content"| WC
The real attack chain is often a multi-step sequence with dependencies (such as: discover the injection point → obtain the credentials → horizontal → escalate privileges). Sending these in parallel at once will only cause chaos. The planner therefore holds a planning to-do list that is retained by task and shared across wake-ups:
- The planner is event-driven - it is woken up as soon as the graph changes, but each wakeup is a new session; the shared todolist allows it to record a serial utilization chain once, and then gradually dispatch intentions according to dependencies in subsequent rounds, instead of pre-expanding the entire chain in one round;
- In each round, only the next step intention is dispatched for "the previous steps have been completed and the fact that it depends on already exists", and the list is updated as the progress progresses (steps that have been satisfied by the fact are marked as completed).
flowchart TB
subgraph TODO["Shared todolist (reserved by task·resident across wakes)"]
direction LR
T1["1 injection point [Completed]"]
T2["2 Obtain Credentials [In Progress]"]
T3["3 Horizontal [To be fronted]"]
T4["4 Privilege Elevation [To be pre-installed]"]
T1 -.Pre-satisfied.-> T2 -.-> T3 -.-> T4
end
R1["The 1st round of awakening faction intention①"] --> T1
R2["Round 2 (①Output fact) Faction intention②"] --> T2
R3["Round 3 (②Output fact) Faction intention③"] --> T3
Therefore, the attack chain still advances stably, without duplication and sequence** in the "event-driven + stateless session" environment - this is the key to ARTEX being able to autonomously complete the multi-step exploitation chain.
Scan the QR code to follow the WeChat official account SecSentry, and send private messages in the background of the official account to join the group for communication.
https://github.com/oritera/Cairn
This project is licensed under GNU Affero General Public License v3.0 (AGPL-3.0). For complete terms, please see the LICENSE file in the root directory of the warehouse.
This means that anyone can freely use, modify and distribute this project, but derivative works must also be open source under AGPL-3.0; in particular, if you modify this project and provide it to users through the network (such as deploying it as an online service), you must also disclose the corresponding complete source code to these users.
⚠️ IMPORTANT NOTE: The open source agreement itself does not restrict the use of the software. The following "Usage Restrictions" and "Disclaimer" are additional agreements and solemn statements from the author to users, please be sure to abide by them.
**ARTEX is only for personal learning, code research and local technical verification, and may not be used to initiate actual testing of any online system or website. **
- Can only be used to read, study and research the source code of this project, and conduct technical principle verification in local isolation environment;
- Suitable for non-offensive purposes such as personal study, academic research, code review, etc.
- It is strictly prohibited to use this tool to scan, detect, exploit or attack any website, online service or networked system (whether authorized or not, whether it is its own property);
- It is strictly prohibited to use this tool for any actual penetration testing, offensive and defensive confrontation, or production environment;
- It is strictly prohibited to use this tool for illegal intrusion, data theft, extortion, denial of service or any destructive or criminal activities;
- It is strictly prohibited to use this tool to engage in activities that violate the laws and regulations of the country/region where you are located.
Users must abide by all laws and regulations on network security, data protection and computer crime in the country/region where they are located (including but not limited to the "Cybersecurity Law", "Data Security Law", "Personal Information Protection Law" and relevant judicial interpretations in mainland China). **All legal responsibilities and consequences arising from the use of this tool are borne by the user. **
This item is provided "AS IS" without warranty of any kind, either express or implied. The authors and contributors are not responsible for any direct or indirect losses, data loss, system damage or legal disputes resulting from the use of this tool (regardless of whether it is used improperly). **Downloading, installing or using this project means that you have read, understood and agreed to all the above terms. **