# How to Create Custom YAML Addons for the LazyOwn Framework: A Complete Development Guide

> Learn how to create custom YAML addons for the LazyOwn framework. Extend LazyOwn capabilities with Lua and YAML for automatic discovery and registration.

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

---

**LazyOwn extends its capabilities by pairing a Lua implementation file with a YAML metadata descriptor in the `plugins/` directory, enabling automatic discovery, permission validation, and CLI registration without modifying core framework code.**

The LazyOwn framework by grisuno uses a modular plugin architecture that allows security researchers to add functionality through custom YAML addons. Each addon consists of two files—a Lua script containing the logic and a YAML file supplying metadata—that reside in the `plugins/` directory. This guide explains how to structure these descriptors and implement the corresponding Lua functions to extend LazyOwn for Red Team engagements.

## How LazyOwn Discovers and Loads Custom Addons

According to the grisuno/lazyown source code, the framework initializes its plugin system by scanning the `plugins/` directory at startup. The `utils.load_plugins()` function iterates through all `*.yaml` files, parsing each with `yaml.safe_load` to extract metadata into a `Plugin` object.

When a user executes `lazyown <plugin_name>`, the framework:

1. Locates the YAML descriptor matching the command name
2. Validates required system capabilities and permissions
3. Imports the corresponding Lua file (same base name as the YAML)
4. Invokes the registered function and handles declared outputs

This auto-discovery mechanism ensures that adding functionality requires no changes to [`lazyc2.py`](https://github.com/grisuno/lazyown/blob/main/lazyc2.py) or other core loader files.

## Structure of a YAML Addon Descriptor

The YAML file supplies critical metadata that controls how LazyOwn exposes and executes the plugin. The descriptor must follow strict indentation rules and include specific fields to pass validation.

### Required Metadata Fields

Every YAML addon must define these core properties:

- **`name`** – The CLI identifier used to invoke the command (e.g., `ping_host`)
- **`description`** – A multiline explanation using the `>` folded style scalar
- **`author`** and **`version`** – Attribution and semantic versioning
- **`enabled`** – Boolean flag determining if the plugin is active
- **`tags`** – Categorization labels for search and autocompletion

### Permission and Dependency Declarations

Security-sensitive operations require explicit capability flags:

- **`permissions`** – List of capability flags such as `needs_file_read`, `needs_file_write`, or `needs_network`
- **`requires_root`** – Boolean indicating if elevated privileges are mandatory
- **`dependencies`** – External binaries or Python packages that must exist on the system
- **`outputs`** – Declared return types (`console_output`, file paths, or binary data)

The **`params`** field defines the plugin's interface, listing expected arguments with `name`, `description`, and `required` boolean flags.

## Step-by-Step: Building Your First Custom Addon

Creating a functional addon requires two files with identical base names placed in `plugins/`.

### Step 1: Write the Lua Implementation

Create a Lua file that implements the core logic and registers the command. In [`plugins/ping.lua`](https://github.com/grisuno/lazyown/blob/main/plugins/ping.lua), the function accesses parameters through `app.params` and returns a string:

```lua
function ping_host()
    local target = app.params["target"]
    if not target then
        return "Error: 'target' parameter missing."
    end

    local cmd = "ping -c 1 " .. target
    local result = io.popen(cmd):read("*a")
    return result
end

register_command("ping_host", ping_host)

```

### Step 2: Create the Matching YAML File

The descriptor [`plugins/ping.yaml`](https://github.com/grisuno/lazyown/blob/main/plugins/ping.yaml) must share the same base name (`ping`) and define the metadata:

```yaml
name: ping_host
description: >
  Sends a single ICMP ping to the supplied target and returns the raw output.
author: "LazyOwn RedTeam"
version: "1.0"
enabled: true
tags:
  - network
  - recon
params:
  - name: target
    description: IP address or hostname to ping
    required: true
permissions:
  - needs_file_read
requires_root: false
dependencies: []
outputs:
  - console_output
notes: >
  Useful for quick reachability checks during a penetration test.

```

### Step 3: Validate and Test

After placing both files in the `plugins/` directory, launch LazyOwn and execute:

```bash
lazyown ping_host --target 8.8.8.8

```

The framework validates the `needs_file_read` permission, loads [`ping.lua`](https://github.com/grisuno/lazyown/blob/main/ping.lua), and prints the command output.

## Real-World Reference: Complex Payload Generation

For advanced use cases, examine the existing `generate_reverse_shell` implementation in the repository. The Lua logic in [`plugins/generate_reverse_shell.lua`](https://github.com/grisuno/lazyown/blob/main/plugins/generate_reverse_shell.lua) handles parameter parsing and payload construction, while [`plugins/generate_reverse_shell.yaml`](https://github.com/grisuno/lazyown/blob/main/plugins/generate_reverse_shell.yaml) declares multiple parameters (`lhost`, `lport`) and binary output types. This pattern demonstrates how to handle complex inputs and declare external tool dependencies in the YAML descriptor.

## Summary

- **LazyOwn uses paired files**: Every addon requires a Lua implementation and a YAML descriptor with matching base names in `plugins/`.
- **YAML drives the framework**: The descriptor controls auto-discovery via `utils.load_plugins()`, permission validation, and CLI exposure through `register_command()`.
- **Security is declarative**: Fields like `permissions`, `requires_root`, and `dependencies` allow LazyOwn to validate system capabilities before executing foreign code.
- **Documentation is automatic**: The framework generates [`plugins/README.md`](https://github.com/grisuno/lazyown/blob/main/plugins/README.md) directly from YAML metadata fields.

## Frequently Asked Questions

### What file naming convention must I follow for LazyOwn addons?

The Lua file and YAML descriptor must share identical base names (e.g., [`myaddon.lua`](https://github.com/grisuno/lazyown/blob/main/myaddon.lua) and [`myaddon.yaml`](https://github.com/grisuno/lazyown/blob/main/myaddon.yaml)) and reside in the `plugins/` directory. LazyOwn pairs these files automatically based on the basename during the discovery phase.

### Can I create addons that require root privileges?

Yes. Set `requires_root: true` in the YAML descriptor and include appropriate permission flags like `needs_file_write` or `needs_network`. The framework checks these declarations before invoking the Lua function to ensure the execution context meets security requirements.

### How does LazyOwn handle missing dependencies?

The `dependencies` field in the YAML descriptor lists required external tools or Python packages. While the analysis shows this field is parsed during plugin registration, you should verify that any declared binaries exist on the target system, as the framework uses this metadata for pre-execution validation.

### Where can I find a template to start building addons?

The repository includes `plugins/01 - template_plugins_yaml.yaml`, which provides the minimal skeleton structure required for valid descriptors. Additionally, [`plugins/README.md`](https://github.com/grisuno/lazyown/blob/main/plugins/README.md) contains the official development guide and checklist for creating compliant addons.