Plugin Development

Complete Guide for Developing Plugins for Mini-B

In versions after 3.6.2, Mini Bucket supports installing and using plugins.

Template:

For example, you can download a plugin template or use it for development: https://mini-bucket.ru/project/plugin-template/

Or you can look at the finished plugin, it’s different from the template, but it also works: https://mini-bucket.ru/project/log-manager/

📋 Table of Contents

  1. Introduction
  2. Plugin Structure
  3. Naming Rules (CRITICAL)
  4. Step-by-Step Plugin Creation
  5. File Descriptions
  6. JavaScript API
  7. PHP API
  8. Database
  9. Complete Plugin Example
  10. Installation and Testing

Introduction

What is a Mini-B Plugin?

A plugin is an extension of Mini-B functionality that can:

  • Run on the local host (where Mini-B is installed)
  • Run on remote hosts (via API)
  • Automatically install on remote servers
  • Have its own SQLite database
  • Use system APIs and authentication

Plugin Requirements

  • PHP 7.0 or higher
  • Write permissions in plugin directory
  • Host must have an API key

Plugin Structure

Minimal Structure

Bash
plugin_name/                      # Plugin folder (REQUIRED)

├── info.json                     # Plugin metadata (REQUIRED)
├── plugin_name.php               # Main UI file (REQUIRED)
├── plugin_name_api.php           # API backend file (REQUIRED)
├── init.php                      # Initialization file (REQUIRED)
├── plugin.js                     # JavaScript core (REQUIRED)
├── plugin.css                    # Styles (REQUIRED)

└── [additional files]            # Optional
    └── assets/                   # Additional resources
        ├── images/
        └── fonts/





Naming Rules (CRITICAL)

⚠️ Golden Rule: ALL NAMES MUST MATCH!

This is the most important part. If names don’t match – the plugin WILL NOT WORK!

Where and what names must match:

#Where to specifyWhat to specifyMust match with
1Plugin folder namemy_pluginAll below ↓
2info.json → sysname"my_plugin"Folder name
3plugin_name.phpmy_plugin.phpFolder name
4plugin_name_api.phpmy_plugin_api.phpFolder name
5plugin.jsplugin.jsAlways this (don’t change)
6plugin.cssplugin.cssAlways this (don’t change)
7init.phpinit.phpAlways this (don’t change)
8menu/plugin_name.phpmy_plugin.phpFolder name
9URL in menuplugins/my_plugin/my_plugin.phpFolder name
10window.pluginName'my_plugin'Folder name

📝 Example of Correct Naming:

Let’s say you’re creating a plugin named log_manager:

Bash
 CORRECT:
Frontend
/var/www/html/admin/plugins/log_manager/              # Folder
/var/www/html/admin/plugins/log_manager/log_manager.php
/var/www/html/admin/plugins/log_manager/log_manager_api.php
/var/www/html/admin/plugins/log_manager/log_manager/init.php
/var/www/html/admin/plugins/log_manager/plugin.js
/var/www/html/admin/plugins/log_manager/plugin.css
/var/www/html/admin/plugins/menu/log_manager.php

Backend
/var/www/minib/plugins/installed/log_manager/info.json     # sysname: "log_manager"
/var/www/minib/plugins/installed/log_manager/uninstall.sh
/var/www/minib/plugins/installed/log_manager/log_manager.db

 INCORRECT:
/var/www/html/admin/plugins/log_manager/                # Case mismatch
/var/www/html/admin/plugins/log_manager/info.json       # sysname: "logmanager" (different)
/var/www/html/admin/plugins/log_manager/index.php       # Wrong file name
/var/www/html/admin/plugins/log_manager/log_manager.db
/var/www/html/admin/plugins/log_manager/info.json
/var/www/html/admin/plugins/log_manager/uninstall.sh

⚠️ For security reasons, do not place executable .sh scripts, downloadable files (logs, temporary files, reports), databases, or warcry files that can be run without authorization in the web directory /var/www/html/admin/plugins/plugin_name.

For such files, use the directory /var/www/minib/plugins/installed/plugin_name.

This directory is located outside the web server.


Step-by-Step Plugin Creation

Step 1: Create plugin folder backend

Bash
cd /var/www/minib/plugins/installed/
mkdir my_plugin
cd my_plugin




Step 2: Create info.json

JSON
{
    "name": "My Awesome Plugin",
    "sysname": "my_plugin",
    "version": "1.0.0",
    "multinet": "Yes",
    "release_date": "2026-06-07",
    "author": "Your Name",
    "description": "Plugin description here",
    "php_version": ">=7.4",
    "os": "Debian/Ubuntu",
    "icon": "fas fa-rocket",
    "icon_bg": "#667eea",
    "group": "tools",
    "group_title": "Tools"
}

Create plugin folder backend frontend

Bash
cd /var/www/html/admin/plugins
mkdir my_plugin
cd my_plugin

Step 3: Create init.php

PHP
<?php
$init = require_once __DIR__ . '/../plugin_template/init.php';
return $init;

OR write your own (copy from template)

Step 4: Create my_plugin.php

php

<?php
$init = require_once __DIR__ . '/init.php';
extract($init);
?>
<!DOCTYPE html>
<html>
<head>
    <title>Mini-B - <?php echo $plugin_name; ?></title>
    <!-- Include system libs -->
    <link href="../../lib/bootstrap-5.3.8-dist/css/bootstrap.min.css" rel="stylesheet">
    <link rel="stylesheet" href="../../lib/fontawesome-free-6.7.2-web/css/all.min.css">
    <link rel="stylesheet" href="../../style.css">
    <link rel="stylesheet" href="../../css/loader.css">
    
    <!-- Plugin assets -->
    <link rel="stylesheet" href="plugin.css">
    <script src="../../js/hosts_load.js"></script>
    <script src="../../js/crt_checker.js"></script>
    
    <!-- API Config -->
    <script>
    window.apiConfig = <?php echo json_encode($js_config); ?>;
    window.pluginName = 'my_plugin';
    </script>
</head>
<body>
    <?php echo $menu; ?>
    
    <main class="main-content">
        <div id="alertContainer"></div>
        
        <div class="widget">
            <div class="widget-header">
                <h3><i class="fas fa-rocket"></i> <?php echo $plugin_name; ?></h3>
            </div>
            <div class="widget-body">
                <p>Your plugin content here</p>
                <button class="btn btn-primary" onclick="testAPI()">Test API</button>
            </div>
        </div>
    </main>
    
    <script src="../../lib/jquery-3.6.0-master/dist/jquery.min.js"></script>
    <script src="../../lib/bootstrap-5.3.8-dist/js/bootstrap.bundle.min.js"></script>
    <script src="../../js/loader.js"></script>
    <script src="plugin.js"></script>
    
    <script>
    function initPlugin() {
        console.log('Plugin initialized');
        showAlert('Plugin is ready!', 'success');
    }
    
    async function testAPI() {
        const result = await apiCall('test', 'GET');
        showAlert(JSON.stringify(result), 'info');
    }
    </script>
</body>
</html>

Step 5: Create my_plugin_api.php

PHP
<?php
define('ROOT_PATH', dirname(dirname(dirname(__FILE__))));
require_once ROOT_PATH . '/config.php';

if (session_status() === PHP_SESSION_NONE) {
    session_start();
}

header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json");

// Validate API key
function validateApiKey() {
    global $db;
    if (!$db) $db = getDB();
    
    $headers = getallheaders();
    $apiKey = $headers['X-API-Key'] ?? $_GET['api_key'] ?? '';
    
    if (empty($apiKey) && isset($_SESSION['user_id'])) {
        return true;
    }
    
    if (empty($apiKey)) {
        http_response_code(401);
        echo json_encode(['error' => 'API key required']);
        exit;
    }
    
    $stmt = $db->prepare("SELECT idHost FROM hosts WHERE hostApiKey = :key");
    $stmt->bindValue(':key', $apiKey, SQLITE3_TEXT);
    $result = $stmt->execute();
    
    if (!$result->fetchArray()) {
        http_response_code(403);
        echo json_encode(['error' => 'Invalid API key']);
        exit;
    }
    
    return true;
}

validateApiKey();

$action = $_GET['action'] ?? $_POST['action'] ?? '';

switch ($action) {
    case 'get_plugin_status':
        echo json_encode(['success' => true, 'available' => true]);
        break;
        
    case 'test':
        echo json_encode(['success' => true, 'message' => 'API is working!']);
        break;
        
    default:
        echo json_encode(['success' => false, 'error' => 'Unknown action']);
}
?>

Step 6: Copy plugin.js and plugin.css

Copy from template (they are universal)

Step 7: Create my_plugin.php

PHP
<?php
return [
    'enabled' => true,
    'group' => 'tools',
    'group_title' => 'Tools',
    'title' => 'My Awesome Plugin',
    'url' => '/plugins/my_plugin/my_plugin.php',
    'icon' => 'fas fa-rocket',
];




Step 8: Install bash script

PHP
#!/bin/bash

# Paths
MENU_DIR="/var/www/html/admin/plugins/menu"
ADMIN_DIR="/var/www/html/admin/plugins/my_plugin"
MINIB_DIR="/var/www/minib/plugins/installed/my_plugin"

# Creating folder
mkdir -p $ADMIN_DIR
mkdir -p $MINIB_DIR

# Assigning the owner and rights
chown -R www-data:www-data $ADMIN_DIR
chown -R www-data:www-data $MINIB_DIR
chmod -R 755 $ADMIN_DIR
chmod -R 755 $MINIB_DIR
chmod +x uninstall.sh

# copy file in admin
cp -p my_plugin.php $ADMIN_DIR/
cp -p my_plugin_api.php $ADMIN_DIR/
cp -p init.php $ADMIN_DIR/
cp -p plugin.css $ADMIN_DIR/
cp -p plugin.js $ADMIN_DIR/
cp -p menu/my_plugin.php $MENU_DIR/

# copy file in minib
cp -p info.json $MINIB_DIR/
cp -p uninstall.sh $MINIB_DIR/

# Delete instalation folder
rm -rf /var/www/minib/plugins/extract/my_plugin*




Step 9. uninstall file

PHP
#!/bin/bash

MENU_DIR="/var/www/html/admin/plugins/menu"
ADMIN_DIR="/var/www/html/admin/plugins/my_plugin"
MINIB_DIR="/var/www/minib/plugins/installed/my_plugin"

rm -rf $ADMIN_DIR
rm -rf $MINIB_DIR
rm $MENU_DIR/my_plugin.php

Final structure installation ZIP archive.

my_plugin_v0.0.1.zip/

├── info.json                     # Plugin metadata (REQUIRED)
├── my_plugin.php                 # Main UI file (REQUIRED)
├── my_plugin_api.php                 # API backend file (REQUIRED)
├── init.php                      # Initialization file (REQUIRED)
├── plugin.js                     # JavaScript core (REQUIRED)
├── plugin.css                    # Styles (REQUIRED)
├── install.sh
├── uninstall.sh
├── menu/
│   └── my_plugin.php

└── [additional files]            # Optional
    └── assets/                   # Additional resources
        ├── images/
        └── fonts/


File Descriptions

📄 info.json

Purpose: Plugin metadata

Required fields:

JSON
{
    "name": "Display Name",           // Display name of the plugin
    "sysname": "plugin_name",         // System name (MUST match folder!)
    "version": "1.0.0",               // Version
    "multinet": "Yes",                // Multi-host support (Yes/No)
    "release_date": "2026-06-07",     // Release date
    "author": "Author Name",          // Author
    "description": "Description",     // Description (HTML supported)
    "php_version": ">=7.4",           // Required PHP version
    "os": "Debian/Ubuntu",            // Supported OS
    "icon": "fas fa-code",            // Font Awesome icon
    "icon_bg": "linear-gradient(...)", // Icon color or gradient
    "group": "tools",                 // Menu group (tools, system, network)
    "group_title": "Tools"            // Group display name
}




Menu groups:

  • Panel
  • Storage
  • Resources
  • Users
  • Security
  • Tools
  • System

📄 init.php

Purpose: Plugin initialization

What it does:

  • Includes system configuration
  • Verifies authentication
  • Gets current host data
  • Configures API URLs (local/remote)
  • Creates JavaScript configuration
  • Defines plugin constants

Returned variables:

PHP
return [
    'db' => $db,                    // System DB connection
    'host' => $host,                // Host data
    'api_key' => $api_key,          // API key
    'js_config' => $js_config,      // JS config
    'menu' => $menu,                // System menu
    'current_host_id' => $id,       // Current host ID
    'plugin_name' => 'Plugin Name'  // Plugin name
];

📄 plugin_name.php

Purpose: Main UI file

What it does:

  • Displays plugin interface
  • Includes system libraries
  • Injects API configuration
  • Calls JavaScript initialization

Key points:

PHP
// Required: Set plugin name for JS
<script>
window.pluginName = 'my_plugin';  // MUST match sysname!
</script>

// Required: Include plugin.js
<script src="plugin.js"></script>




📄 plugin_name_api.php

Purpose: Plugin API backend

What it does:

  • Handles AJAX requests
  • Validates API keys
  • Works with database
  • Returns JSON responses

Required actions:

PHP
case 'get_plugin_status':  // REQUIRED for remote installation!
    echo json_encode(['success' => true, 'available' => true]);
    break;




📄 plugin.js

Purpose: Universal JavaScript core

Provides:

  • apiCall() – universal API call
  • showAlert() – notifications
  • escapeHtml() – XSS protection
  • checkRemotePluginAvailability() – plugin check
  • showPluginInstallModal() – remote installation

Do not modify this file! It’s universal for all plugins.

📄 plugin.css

Purpose: Plugin styles

What you can add:

  • Custom CSS classes
  • Animations
  • Responsive styles

📄 menu_entry.php

Purpose: System menu entry

Structure:

PHP
<?php
return [
    'enabled' => true,              // Enabled/disabled
    'group' => 'tools',             // Menu group
    'group_title' => 'Tools',       // Group display name
    'title' => 'Plugin Title',      // Display name
    'url' => '/plugins/my_plugin/my_plugin.php',  // URL (MUST match!)
    'icon' => 'fas fa-code',        // Icon
];





JavaScript API

Core Functions

apiCall(action, method, data)

Universal API call.

JavaScript
// GET request
const result = await apiCall('get_data', 'GET');

// GET with parameters
const result = await apiCall('get_item', 'GET', { id: 5 });

// POST request
const result = await apiCall('save_data', 'POST', { 
    name: 'test', 
    value: 123 
});




showAlert(message, type)

Show notification.

JavaScript
showAlert('Success!', 'success');
showAlert('Error!', 'danger');
showAlert('Warning!', 'warning');
showAlert('Info!', 'info');




escapeHtml(str)

XSS protection.

JavaScript
const safe = escapeHtml('<script>alert("xss")</script>');




Helper Functions

JavaScript
// Format file size
formatFileSize(1024);  // "1 KB"

// Format date
formatDate(1686123456);  // "2023-06-07 12:34:56"

// Highlight search term
highlightSearch('text with search term', 'search');  // 'text with <span class="search-highlight">search</span> term'

// Debounce for search
const searchHandler = debounce(() => {
    // Search logic
}, 300);




Plugin Lifecycle

JavaScript
// Called automatically after loading
function initPlugin() {
    console.log('Plugin ready');
    // Your initialization code
}

// Access configuration
console.log(window.apiConfig);
// {
//     pluginApiUrl: '...',
//     apiBaseUrl: '...',
//     apiKey: '...',
//     isLocalhost: true/false,
//     currentHostId: 1,
//     pluginName: 'my_plugin'
// }





PHP API

Standard API Functions

PHP
// API key validation (REQUIRED)
function validateApiKey() { ... }

// Initialize plugin database
function initPluginDB() { ... }

// Get status (REQUIRED)
function getPluginStatus() {
    return ['success' => true, 'available' => true];
}




PHP
$action = $_GET['action'] ?? $_POST['action'] ?? '';

switch ($action) {
    case 'get_plugin_status':  // REQUIRED
        echo json_encode(getPluginStatus());
        break;
        
    case 'my_action':  // Your actions
        // Processing
        echo json_encode(['success' => true]);
        break;
        
    default:
        echo json_encode(['success' => false, 'error' => 'Unknown action']);
}




Database Operations

PHP
// Create/connect to database
function initPluginDB() {
    $db = new SQLite3(PLUGIN_DIR . '/plugin_name.db');
    
    // Create tables
    $db->exec("
        CREATE TABLE IF NOT EXISTS my_table (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            data TEXT NOT NULL,
            created_at DATETIME DEFAULT CURRENT_TIMESTAMP
        )
    ");
    
    return $db;
}

// Example query
function getData() {
    $db = initPluginDB();
    $result = $db->query("SELECT * FROM my_table");
    $data = [];
    while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
        $data[] = $row;
    }
    $db->close();
    return $data;
}





Database

Where to Store DB

PHP
define('PLUGIN_DIR', dirname(__FILE__));
define('PLUGIN_DB', PLUGIN_DIR . '/plugin_name.db');




File Permissions

PHP
// After creating DB
chmod($db_path, 0666);




Example Database Structure

SQL
-- Settings table
CREATE TABLE IF NOT EXISTS settings (
    key TEXT PRIMARY KEY,
    value TEXT,
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

-- Data table
CREATE TABLE IF NOT EXISTS items (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    status INTEGER DEFAULT 1,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);





Complete Plugin Example

Todo List Plugin

info.json:

JSON
{
    "name": "Todo List",
    "sysname": "todo_list",
    "version": "1.0.0",
    "multinet": "Yes",
    "release_date": "2026-06-07",
    "author": "Mini-B Team",
    "description": "Simple todo list plugin",
    "php_version": ">=7.4",
    "os": "Debian/Ubuntu",
    "icon": "fas fa-tasks",
    "group": "tools",
    "group_title": "Tools"
}




todo_list_api.php:

JSON
<?php
define('PLUGIN_DIR', dirname(__FILE__));
define('TODO_DB', PLUGIN_DIR . '/todo_list.db');

function initDB() {
    $db = new SQLite3(TODO_DB);
    $db->exec("
        CREATE TABLE IF NOT EXISTS todos (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            title TEXT NOT NULL,
            completed INTEGER DEFAULT 0,
            created_at DATETIME DEFAULT CURRENT_TIMESTAMP
        )
    ");
    return $db;
}

function getTodos() {
    $db = initDB();
    $result = $db->query("SELECT * FROM todos ORDER BY created_at DESC");
    $todos = [];
    while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
        $todos[] = $row;
    }
    $db->close();
    return $todos;
}

function addTodo($title) {
    $db = initDB();
    $stmt = $db->prepare("INSERT INTO todos (title) VALUES (:title)");
    $stmt->bindValue(':title', $title, SQLITE3_TEXT);
    $stmt->execute();
    $id = $db->lastInsertRowID();
    $db->close();
    return ['success' => true, 'id' => $id];
}

function toggleTodo($id) {
    $db = initDB();
    $stmt = $db->prepare("UPDATE todos SET completed = NOT completed WHERE id = :id");
    $stmt->bindValue(':id', $id, SQLITE3_INTEGER);
    $stmt->execute();
    $db->close();
    return ['success' => true];
}

function deleteTodo($id) {
    $db = initDB();
    $stmt = $db->prepare("DELETE FROM todos WHERE id = :id");
    $stmt->bindValue(':id', $id, SQLITE3_INTEGER);
    $stmt->execute();
    $db->close();
    return ['success' => true];
}

$action = $_GET['action'] ?? $_POST['action'] ?? '';

switch ($action) {
    case 'get_plugin_status':
        echo json_encode(['success' => true, 'available' => true]);
        break;
    case 'get_todos':
        echo json_encode(['success' => true, 'data' => getTodos()]);
        break;
    case 'add_todo':
        $title = $_POST['title'] ?? '';
        echo json_encode(addTodo($title));
        break;
    case 'toggle_todo':
        $id = $_POST['id'] ?? 0;
        echo json_encode(toggleTodo($id));
        break;
    case 'delete_todo':
        $id = $_POST['id'] ?? 0;
        echo json_encode(deleteTodo($id));
        break;
}
?>





Installation and Testing

Local Installation

  • Copy plugin folder:
JSON
cp -r my_plugin_v0.0.1.zip /var/www/minib/plugins/repositories/

Login control panel > System > Plugins > Repository > Select your plugin and click Install button.

Plugin Installation Principle

A plugin is a ZIP archive that contains:

  • the plugin’s files;
  • an installation script (install.sh);
  • other necessary files required for the plugin to function.

When you upload a plugin to mini-bucket, it is saved to the following directory:
/var/www/minib/plugins/repositories

Once uploaded, the plugin becomes available for installation in the interface under:
System > Plugins > Repository

To install the plugin, click the Install button. This triggers the following actions:

  1. The archive is extracted to /var/www/minib/plugins/extract.
  2. The install.sh file is granted execution permissions.
  3. The install.sh script is executed.

After a successful installation, the plugin appears in the interface under:
System > Plugins > Installed Plugins

Each installed plugin is equipped with a Toggle switch to enable or disable it.

You can also delete the plugin file from the repository by clicking the Delete button.

Important notes:

  • Deleting a plugin from the repository does not remove the plugin from the system.
  • Deleting a plugin from the system (via the Installed Plugins interface) does not delete its ZIP archive from the repository.

Back end installation process.


🎯 Pre-release Checklist

  • Folder name matches sysname in info.json
  • File names plugin_name.php and plugin_name_api.php match folder name
  • window.pluginName set in plugin_name.php
  • get_plugin_status action exists in plugin_name_api.php
  • Correct URL in munu file
  • All files have permissions 644 (755 for folders)
  • Plugin tested on local host
  • Plugin tested on remote host
  • Automatic installation works
  • Documentation updated

📞 Support


📄 License

MIT License – freely use, modify, and distribute!


Documentation Version: 1.0.0
Last Updated: 2026-06-07
Author: Mini-B Development