Skip to content
NiceTryBot DOCS

Extending

Creating Add-ons

Introduction

Everything NiceTryBot's own add-ons can do, yours can too. An add-on is a Composer package with a service provider. It may add pages to the sidebar and settings, panels to a domain's page, obstacles to the gym, fields to the rule language, columns to Activity, and tools to the MCP server, and it can say how long the visitor data it keeps should live.

If you have written a Laravel package before, you will feel right at home. NiceTryBot's control panel is a Laravel application, and add-ons are discovered using Laravel's package discovery.

Creating Add-ons Using AI

The extension points below are regular and well-defined, which makes add-ons a natural job for a coding agent. Open your agent in a checkout of NiceTryBot and give it a prompt like the following:

Read https://docs.nicetrybot.com/docs/0.x/creating-addons.md, then study packages/privacy-mode in this repository as a working example. Help me build a NiceTryBot add-on in packages/ that does the following: ...

Follow the add-on conventions exactly, write Pest tests, and run them before you tell me it's done.

Local Development

To develop an add-on, clone NiceTryBot and set it up locally. You will need PHP 8.5, Composer, and Node 22:

Terminal
git clone https://github.com/NiceTryBot/nicetrybot.git
cd nicetrybot

composer setup
composer dev

NiceTryBot's root composer.json file already includes a path repository for packages/*, so the easiest place to build your add-on is a new directory inside packages. Once its composer.json file exists, require it like any other package:

Terminal
composer require acme/hello

Package Structure

A typical add-on looks like this. Every directory is optional except src:

packages/hello/
├── composer.json
├── config/hello.php
├── database/migrations/
├── resources/views/
│   └── livewire/
├── routes/team.php
├── src/HelloServiceProvider.php
└── tests/Feature/

The Composer File

An add-on's composer.json file must use the nicetry-addon type and register its service provider for discovery. The extra.nicetry section describes the add-on to the control panel:

JSON
{
    "name": "acme/hello",
    "description": "One line that shows on the Add-ons page.",
    "type": "nicetry-addon",
    "version": "1.0.0",
    "license": "Elastic-2.0",
    "autoload": {
        "psr-4": {
            "Acme\\Hello\\": "src/"
        }
    },
    "extra": {
        "nicetry": {
            "name": "Hello",
            "edition": "free"
        },
        "laravel": {
            "providers": [
                "Acme\\Hello\\HelloServiceProvider"
            ]
        }
    }
}

The extra.nicetry section accepts the following keys:

Key Description
name The add-on's display name. Defaults to a headline of its slug.
slug The add-on's slug. Defaults to the part of the package name after the /.
edition Either free, the default, or paid.

The Service Provider

An add-on's service provider extends App\Addons\AddonServiceProvider. Most of what an add-on contributes is declared using properties and methods on this class, and NiceTryBot wires everything up for you:

PHP
<?php

namespace Acme\Hello;

use App\Addons\AddonServiceProvider;
use App\Enums\ViewSlot;
use App\Support\NavItem;

class HelloServiceProvider extends AddonServiceProvider
{
    /**
     * The add-on's route files.
     *
     * @var array<string, string>
     */
    protected array $routes = [
        'team' => 'routes/team.php',
    ];

    /**
     * The views drawn in the control panel's view slots.
     *
     * @var array<string, string>
     */
    protected array $slots = [
        ViewSlot::DomainPanels->value => 'hello::domain-panel',
    ];

    /**
     * The add-on's sidebar links.
     *
     * @return list<NavItem>
     */
    protected function nav(): array
    {
        return [
            new NavItem('Hello', 'hand-raised', 'hello.index', order: 75),
        ];
    }
}

The provider's register and boot methods are reserved for NiceTryBot. If your add-on needs to do anything else, override the registerAddon and bootAddon methods instead:

PHP
/**
 * Bootstrap the add-on.
 */
protected function bootAddon(): void
{
    // ...
}

Conventions

Like Laravel itself, add-ons favour convention over configuration. NiceTryBot loads the following from your package's root automatically, using the add-on's slug:

  • config/{slug}.php is merged into config('{slug}').
  • database/migrations run alongside NiceTryBot's own migrations.
  • resources/views are available under the {slug}:: namespace.
  • resources/views/livewire become Livewire components under the {slug}:: namespace.
  • gym/obstacles/{Vendor}/{Name} holds the front-end of any gym obstacles.

Routes

Add-ons declare their route files using the $routes property. Each key determines how the routes are loaded:

Key Description
team Signed-in pages, served under the current team, such as /{team}/hello. They use the web, auth, and verified middleware and require team membership.
gym The visitor's side of the gym, served under the gym path. These routes have no session, CSRF protection, or Livewire.
web Loaded as-is.

For example, the following routes/team.php file serves a Livewire page from the add-on's resources/views/livewire directory:

PHP
use Illuminate\Support\Facades\Route;

Route::livewire('hello', 'hello::index')->name('hello.index');

Every route an add-on registers returns a 404 while the add-on is turned off, so you never need to check for that yourself.

Add-ons may add links to three menus by overriding the corresponding method on their service provider:

Method Returns Menu
nav list<App\Support\NavItem> The sidebar.
settingsNav list<App\Support\SettingsNavItem> The Settings menu.
workspaceMenu list<App\Support\WorkspaceMenuItem> The cog menu in the top right.

Each item accepts an order, which determines where it appears among NiceTryBot's own links. Core sidebar links are spaced ten apart, from Overview at 10 to Bot gym at 90, so there is always room to slot yours in between. The cog menu's core links are Domains & origins at 10 and Visitor data at 20.

View Slots

View slots are places in the control panel where an add-on may draw its own views. Map a slot to a view, or a list of views, using the $slots property:

Slot Where it draws
head The <head> of every control panel page.
public-nav Buttons in the public header.
welcome-sections Sections on the welcome page.
login-actions Below the login form.
app-banner Notices above signed-in pages.
topbar-actions Small buttons before the cog menu.
body-end Scripts and modals at the end of the <body>.
domains-actions Buttons beside Add domain. Receives $team.
domains-sections Sections on the Domains & origins page. Receives $team.
domain-panels Panels in a domain page's side column. Receives $domain.
rules-gallery Below the rule template gallery. Receives $team.
rule-panels Below Send its clients to the gym when editing a rule. Receives $team and $rule.

Gym Obstacles

An add-on may add obstacles to the bot gym by listing them in the $obstacles property. Each obstacle implements the App\Gym\Contracts\Obstacle interface, and most obstacles extend App\Gym\BaseObstacle, which provides sensible defaults:

PHP
protected array $obstacles = [
    StretchingObstacle::class,
];

The interface asks an obstacle to describe itself to the routine editor, and to run its challenge for the visitor:

Method Description
key() A stable key that routines store, such as proof-of-work. Never change it once released.
name(), tagline(), icon() How the obstacle appears in the routine editor. Icons are Heroicon names.
settings(), configure(), summarize() The obstacle's settings, and a short summary of a configured obstacle.
humanSeconds(), workHashes() How long the obstacle takes a person, and how much work it costs a bot.
begin(), view(), viewData() Start the challenge and render it.
attempt() Check a visitor's answer, returning an ObstacleOutcome.

The obstacles that ship with NiceTryBot, in gym/obstacles/NiceTryBot, are the best reference for writing your own.

Rule Fields

Add-ons may add fields to the rule language and Activity search. List a backed enum implementing App\Support\RuleFieldDefinition in the $ruleFields property, and each case becomes a field. The interface's methods describe the database column behind each field, whether it holds text or numbers, and the English name and hint shown in the editor. The Tarpit add-on's tarpit and tarpit.outcome fields are a good example.

Add-ons may also offer ready-made rules and gym routines through the $ruleTemplates and $routineTemplates properties.

Visitor Data

If your add-on stores anything about visitors, it must tell NiceTryBot how to keep that data in check. List a class implementing App\Support\VisitorDataHolder in the $visitorData property. It answers three questions:

Method Description
retention() How long each kind of data the add-on stores is kept.
prune() Delete a team's data older than its retention period.
forget() Erase everything stored about a visitor, by IP address or fingerprint.

This keeps your add-on's data inside the retention periods and erasure requests described in the privacy documentation, and those duties continue even while the add-on is turned off.

Other Extension Points

The service provider offers a number of other properties for more specialised add-ons:

Property Description
$policies Authorization policies to register, keyed by model.
$mcpTools Tools to add to the control panel's MCP server.
$requestAddons Marks, details, and actions for requests in Activity.
$actionAddons Badges for Activity's action column.
$clientAddons Extra details for client cells.
$edgeLineAddons Keep your own data from each line of the edge's access log.
$attemptListeners Listen to every obstacle attempt in the gym.
$gymAddons Add a setting to the routine editor, and HTML to the gym's page.
$captureFeed, $lockdownFeed, $tarpitFeed Feed instructions to the edge.

To see these in use, the free add-ons in NiceTryBot's packages directory are complete, working examples. packages/privacy-mode is the smallest place to start, packages/tarpit shows rule fields and edge feeds, and packages/lockdown shows policies, MCP tools, and the cog menu.

Note

Paid add-ons are licence-gated by NiceTryBot automatically when their edition is paid, so add-on authors never write licence code of their own.

Testing Add-ons

NiceTryBot is tested using Pest, and your add-on's tests may live in its own tests directory alongside its code. Before sharing your add-on, run NiceTryBot's checks, which include Pint, PHPStan, and the test suite:

Terminal
composer ci:check

Stuck? Ask your agent.

Every page is plain Markdown for agents like Claude Code, Codex or Cursor. Paste this prompt and it will read this page, then follow links to the rest.

prompt
Read https://docs.nicetrybot.com/docs/0.x/creating-addons.md and use it to help me with NiceTryBot. Treat the docs as the source of truth, and fetch any other page you need from https://docs.nicetrybot.com/llms.txt.