# JSON Structure for Defining .tool Files in LazyOwn: Complete Schema Guide

> Learn the JSON structure for LazyOwn .tool files. Discover mandatory fields, dynamic placeholders like {ip} and {port}, and schema details for efficient tool definition.

- Repository: [Grisuno/lazyown](https://github.com/grisuno/lazyown)
- Tags: api-reference
- Published: 2026-03-02

---

**LazyOwn .tool files require a strict JSON object containing four mandatory fields—`toolname`, `command`, `trigger`, and `active`—where the `command` string supports dynamic placeholders like `{ip}`, `{port}`, and `{outputdir}` for runtime substitution.**

LazyOwn treats every external utility—from Nmap scripts to web fuzzers—as a modular **.tool** file defined in JSON format. Understanding the exact JSON structure for defining .tool files in LazyOwn is essential for extending the framework with custom reconnaissance or exploitation utilities, as the [`pwntomate.py`](https://github.com/grisuno/lazyown/blob/main/pwntomate.py) engine strictly enforces this schema during scan execution.

## Core JSON Schema for LazyOwn .tool Files

Every .tool file must contain a single JSON object with exactly four required keys. The parser in [`main/pwntomate.py`](https://github.com/grisuno/lazyown/blob/main/main/pwntomate.py) raises a `KeyError` if any field is missing, aborting execution immediately.

### Required Fields

- **`toolname`** (string): Human-readable identifier used to construct output filenames. The engine saves results to `{outputdir}/{toolname}.txt`.
- **`command`** (string): The shell command to execute. Must include placeholders for dynamic values like target IPs and ports.
- **`trigger`** (array of strings): Service names (as reported by Nmap) that activate this tool. Use `["all"]` to run against any service.
- **`active`** (boolean): Enable or disable the tool. Inactive tools (`false`) are ignored during scans.

## Available Placeholders for Dynamic Command Construction

The `command` field supports string substitution using Python's `str.replace` method before execution. Valid placeholders include:

- `{ip}` — Target IP address
- `{port}` — Target service port
- `{domain}` — Target domain name
- `{outputdir}` — Base results directory (`args.basedir`)
- `{toolname}` — Value of the `toolname` field
- `{s}` — Inserts `s` for HTTPS or empty string for HTTP
- `{username}` / `{password}` — Credentials from [`sessions/credentials.txt`](https://github.com/grisuno/lazyown/blob/main/sessions/credentials.txt)
- `{usrwordlist}` / `{dirworlist}` — Wordlist paths from [`main/payload.json`](https://github.com/grisuno/lazyown/blob/main/main/payload.json)
- `{ext}` / `{nameserver}` — Domain components for DNS tools

## Real-World .tool File Examples from the Repository

### Web Fuzzing with ffuf.tool

Located at `main/tools/ffuf.tool`:

```json
{
    "toolname": "ffuf_tool",
    "command": "ffuf -u http{s}://{ip}:{port}/FUZZ -w /usr/share/wordlists/dirb/common.txt -mc 200,204,301,302,307,401 -o {outputdir}/{toolname}.txt",
    "trigger": ["http", "https", "http-mgmt", "http-alt"],
    "active": true
}

```

### Kerberos Enumeration with kerbrute.tool

Located at `main/tools/kerbrute.tool`:

```json
{
    "toolname": "kerbrute_tool_user",
    "command": "kerbrute userenum --dc {ip} -d {domain} -t 20 /usr/share/wordlists/SecLists-master/Usernames/xato-net-10-million-usernames.txt | tee {outputdir}/{toolname}.txt",
    "trigger": ["kerberos-sec"],
    "active": true
}

```

### DNS Enumeration with dnsenum.tool

Located at `main/tools/dnsenum.tool`:

```json
{
    "toolname": "dns_enum_tool",
    "command": "dnsenum --dnsserver {ip} --enum {domain} {dnswordlist} > {outputdir}/dnsenum.txt",
    "trigger": ["domain"],
    "active": true
}

```

### Custom Exploit Wrapper (Inactive)

Located at `main/tools/bigbang.tool`:

```json
{
    "toolname": "bigbang",
    "command": "cd /home/grisun0/src/scripts ; python3 exploit_bigbang.py | tee {outputdir}/{toolname}.txt",
    "trigger": ["http", "https", "http-mgmt", "http-alt"],
    "active": false
}

```

## How LazyOwn Processes .tool Files

The execution logic resides in [`main/pwntomate.py`](https://github.com/grisuno/lazyown/blob/main/main/pwntomate.py). During a scan, the engine:

1. Iterates through all `.tool` files in `main/tools/`
2. Parses the JSON and validates the four required fields
3. Checks if `active` is `true` and if the detected service matches any value in `trigger`
4. Performs string substitution on `command` using the placeholder mapping
5. Executes the final shell command and saves output to `{outputdir}/{toolname}.txt`

Optional runtime variables like `{usrwordlist}` and `{dirworlist}` are loaded from [`main/payload.json`](https://github.com/grisuno/lazyown/blob/main/main/payload.json), while credentials (`{username}`, `{password}`) are sourced from [`sessions/credentials.txt`](https://github.com/grisuno/lazyown/blob/main/sessions/credentials.txt) when available.

## Step-by-Step Guide to Creating Custom .tool Files

Follow this checklist when adding new tools to LazyOwn:

1. **Create the JSON file** in `main/tools/` with a `.tool` extension.
2. **Include all four mandatory fields**: `toolname`, `command`, `trigger`, `active`.
3. **Use valid placeholders** from the supported list only; unsupported variables will remain as literal text.
4. **Set `active: true`** to enable automatic execution during scans.
5. **Define accurate triggers** using Nmap service names (e.g., `http`, `ssh`, `microsoft-ds`, `kerberos-sec`).
6. **Test the command manually** by replacing placeholders with real values before committing the file.

When the scanner runs, it will automatically pick up any new, active `.tool` file, inject the proper values, and execute the command only when the service matches a trigger.

## Summary

- LazyOwn `.tool` files use a **strict four-field JSON schema**: `toolname`, `command`, `trigger`, and `active`.
- The `command` field supports **dynamic placeholders** like `{ip}`, `{port}`, and `{outputdir}` for runtime substitution.
- Tools execute only when their `trigger` array matches the detected Nmap service name and `active` is set to `true`.
- The parsing engine in [`main/pwntomate.py`](https://github.com/grisuno/lazyown/blob/main/main/pwntomate.py) validates the schema and performs string replacement before shell execution.
- Custom tools require testing with literal values before deployment to ensure placeholder alignment.

## Frequently Asked Questions

### What happens if a required field is missing from a .tool file?

The LazyOwn parser in [`main/pwntomate.py`](https://github.com/grisuno/lazyown/blob/main/main/pwntomate.py) will raise a `KeyError` and abort execution. All four fields—`toolname`, `command`, `trigger`, and `active`—must be present for the tool to load correctly.

### Can I use custom placeholders not listed in the documentation?

No. The engine only substitutes known placeholders like `{ip}`, `{port}`, and `{domain}`. Any custom variables will remain as literal text in the final command, likely causing execution errors or unexpected behavior.

### How do I run a tool against every service regardless of the port?

Set the `trigger` array to `["all"]`. This special value instructs the engine to execute the command for any detected service, bypassing the normal service-name matching logic in [`pwntomate.py`](https://github.com/grisuno/lazyown/blob/main/pwntomate.py).

### Where does LazyOwn save the output from .tool executions?

Results are written to `{outputdir}/{toolname}.txt`, where `{outputdir}` maps to the base directory specified by `args.basedir` and `{toolname}` is the value defined in the tool's JSON configuration.