> For the complete documentation index, see [llms.txt](https://adaptix-framework.gitbook.io/adaptix-framework/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://adaptix-framework.gitbook.io/adaptix-framework/development/extenders/service-extender.md).

# Service Extender

Service Extender — plugins for extending server functionality: notifications, integrations, automation.

### Flow

<figure><img src="https://2104178602-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS8p8XLFtLmf0NkofQvoa%2Fuploads%2Fgit-blob-8db726db7853e30d93955dffe78f609847334614%2FScreenshot_20260120_205810.png?alt=media" alt=""><figcaption></figcaption></figure>

```
┌─────────────────────────────────────────────────────────────────────────────┐
│                         SERVICE EXTENDER FLOW                               │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  ╔═══════════════════════════════════════════════════════════════════════╗  │
│  ║                        INITIALIZATION                                 ║  │
│  ╚═══════════════════════════════════════════════════════════════════════╝  │
│                                                                             │
│  Server Startup                                                             │
│         │                                                                   │
│         V                                                                   │
│  LoadPluginService(configPath)                                              │
│         │                                                                   │
│         ├──> Load config.yaml                                               │
│         │                                                                   │
│         ├──> Load plugin.so                                                 │
│         │                                                                   │
│         └──> InitPlugin(ts, moduleDir, serviceConfig)                       │
│                     │                                                       │
│                     ├──> [optional] Parse serviceConfig (YAML)              │
│                     │                                                       │
│                     ├──> [optional] Restore data from DB                    │
│                     │         TsExtenderDataLoad(extenderName, key)         │
│                     │                                                       │
│                     ├──> [optional] Register event hooks                    │
│                     │         TsEventHookRegister("agent.new", ...)         │
│                     │         TsEventHookRegister("agent.terminate", ...)   │
│                     │                                                       │
│                     ├──> [optional] Register HTTP endpoints                 │
│                     │         TsEndpointRegister("POST", "/api/...", ...)   │
│                     │                                                       │
│                     └──> return PluginService                               │
│                                                                             │
│  ╔═══════════════════════════════════════════════════════════════════════╗  │
│  ║                        EVENT HANDLING                                 ║  │
│  ╚═══════════════════════════════════════════════════════════════════════╝  │
│                                                                             │
│  Server Event (e.g. agent.new)                                              │
│         │                                                                   │
│         V                                                                   │
│  EventManager.Emit("agent.new", HookPost, eventData)                        │
│         │                                                                   │
│         V                                                                   │
│  Service Hook Handler                                                       │
│         │                                                                   │
│         ├──> Extract event data                                             │
│         │                                                                   │
│         ├──> Process (send notification, log, etc.)                         │
│         │                                                                   │
│         └──> return nil (success) or error                                  │
│                                                                             │
│  ╔═══════════════════════════════════════════════════════════════════════╗  │
│  ║                        GUI INTERACTION                                ║  │
│  ╚═══════════════════════════════════════════════════════════════════════╝  │
│                                                                             │
│  GUI Client ──> POST /service/call (via .axs ax.service_command)            │
│         │                                                                   │
│         V                                                                   │
│  TsServiceCall(serviceName, operator, function, args)                       │
│         │                                                                   │
│         V                                                                   │
│  PluginService.Call(operator, function, args)                               │
│         │                                                                   │
│         ├──> Process request                                                │
│         │                                                                   │
│         └──> TsServiceSendDataClient(operator, service, response)           │
│                     │                                                       │
│                     V                                                       │
│              GUI receives response via WebSocket                            │
│                                                                             │
│  ╔═══════════════════════════════════════════════════════════════════════╗  │
│  ║                        DATA PERSISTENCE                               ║  │
│  ╚═══════════════════════════════════════════════════════════════════════╝  │
│                                                                             │
│  Service needs to save/load config or data                                  │
│         │                                                                   │
│         ├──> TsExtenderDataSave(extenderName, key, value)                   │
│         │                                                                   │
│         └──> TsExtenderDataLoad(extenderName, key)                          │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘
```

### PluginService Interface

```go
type PluginService interface {
    // Call service function from GUI
    Call(operator string, function string, args string)
}
```

### AxScript for Service (ax\_config.axs)

The `.axs` file defines the UI for interacting with the service in GUI (settings dialogs, menus).

**Required functions:**

```javascript
// 1. Service initialization (called on load)
function InitService() {
    // Optional: initial setup
}

// 2. Data handler from server (called when receiving data via TsServiceSendData*)
function data_handler(data) {
    let response = JSON.parse(data);
    
    switch (response.action) {
        case "config":
            // Show configuration dialog
            buildConfigDialog(response);
            break;
        case "result":
            if (response.success) {
                ax.show_message("Success", "Operation completed!");
            } else {
                ax.show_message("Error", response.error);
            }
            break;
    }
}
```

**Optional elements:**

```javascript
// Service metadata
var metadata = {
    name: "My Service",
    description: "Service description"
};

// Add item to Settings menu
let settings_action = menu.create_action("My Service Settings", function() {
    // Request configuration from server
    ax.service_command("my_service", "get_config", {});
});
menu.add_main_settings(settings_action);

// Settings dialog example
function buildConfigDialog(config) {
    let label = form.create_label("API Key:");
    let text_key = form.create_textline(config.api_key || "");
    
    let check_enabled = form.create_check("Enable service");
    check_enabled.setChecked(config.enabled || false);
    
    let layout = form.create_vlayout();
    layout.addWidget(label);
    layout.addWidget(text_key);
    layout.addWidget(check_enabled);
    
    let dialog = form.create_dialog("Service Settings");
    dialog.setSize(400, 200);
    dialog.setLayout(layout);
    
    if (dialog.exec()) {
        // Send new configuration to server
        ax.service_command("my_service", "set_config", {
            api_key: text_key.text(),
            enabled: check_enabled.isChecked()
        });
    }
}
```

**GUI ↔ Server Interaction:**

```
GUI                              Server (Plugin)
 │                                    │
 ├────── ax.service_command ──────────┼──> PluginService.Call(operator, function, args)
 │    (service, function, args)       │
 │                                    │
 │                                    ├──> Process request
 │                                    │
 │<───────────────────────────────────┼── TsServiceSendDataClient(operator, service, data)
 │                                    │
 └─ data_handler(data) <──────────────┘
```

#### Service Config (config.yaml)

```yaml
extender_type: "service"
extender_file: "notifier.so"
ax_file: "ax_config.axs"

service_name: "notifier"
service_config: |
  bot_token: ""
  chat_ids: []
  enabled: false
  events:
    agent.new:
      enabled: true
      template: "*New Agent*\n└ ID: `{AgentId}`"
    agent.activate:
      enabled: true
      template: "*Agent Activated*\n└ ID: `{AgentId}`"
```

***

#### Example Teamserver API for Service Extender

```go
type Teamserver interface {
    // ═══════════════════════════════════════════════════════════════════════
    // EVENT HOOKS
    // ═══════════════════════════════════════════════════════════════════════
    
    // Register event handler
    TsEventHookRegister(eventType string, name string, phase int, priority int, handler func(event any) error) string
    
    // Remove handler by ID
    TsEventHookUnregister(hookID string) bool
    
    // Remove all handlers by name
    TsEventHookUnregisterByName(name string) int
    
    // Simplified Pre/Post hook registration
    TsEventHookOnPre(eventType string, name string, handler func(event any) error) string
    TsEventHookOnPost(eventType string, name string, handler func(event any) error) string
    
    // ═══════════════════════════════════════════════════════════════════════
    // GUI COMMUNICATION
    // ═══════════════════════════════════════════════════════════════════════
    
    // Send data to all clients
    TsServiceSendDataAll(service string, data string)
    
    // Send data to specific client
    TsServiceSendDataClient(operator string, service string, data string)
    
    // ═══════════════════════════════════════════════════════════════════════
    // DATA PERSISTENCE
    // ═══════════════════════════════════════════════════════════════════════
    
    // Save data to DB
    TsExtenderDataSave(extenderName string, key string, value []byte) error
    
    // Load data from DB
    TsExtenderDataLoad(extenderName string, key string) ([]byte, error)
    
    // Delete data
    TsExtenderDataDelete(extenderName string, key string) error
    
    // List keys
    TsExtenderDataKeys(extenderName string) ([]string, error)
    
    // Delete all data
    TsExtenderDataDeleteAll(extenderName string) error
    
    // ═══════════════════════════════════════════════════════════════════════
    // HTTP ENDPOINTS (optional)
    // ═══════════════════════════════════════════════════════════════════════
    
    // Register protected endpoint (with authentication)
    TsEndpointRegister(method string, path string, handler func(username string, body []byte) (int, []byte)) error
    
    // Register public endpoint (without authentication)
    TsEndpointRegisterPublic(method string, path string,  handler func(body []byte) (int, []byte)) error
    
    // Remove endpoints
    TsEndpointUnregister(method string, path string) error
    TsEndpointUnregisterPublic(method string, path string) error
    
    // Check existence
    TsEndpointExists(method string, path string) bool
    TsEndpointExistsPublic(method string, path string) bool
}
```
