Skip to content

Repository files navigation

DOCSight

DOCSight Community Modules

Discover and share modules for DOCSight.


Available Modules

DOCSight can install catalog modules from Settings > Extensions > Community Modules. The catalog is defined in registry.json.

Module Type What it adds Requirements
VF Kabel Deutschland Community Thresholds analysis Regional signal thresholds based on community recommendations for Vodafone/Kabel Deutschland cable connections DOCSight 2026.2+
UDM WAN-Monitor integration WAN1/WAN2 status collection for Ubiquiti UDM Pro/SE setups, with DOCSight events and dashboard surfaces DOCSight 2026.2+, UDM access on the local network

The catalog is curated, but modules can be maintained either in this repository or in an external contributor repository. See Submitting a Module for the registry format and review checklist.


Building a Module

Quick Start

  1. Copy the TEMPLATE/ directory
  2. Rename the directory to your module name
  3. Edit manifest.json with your module's details
  4. Add your code
  5. Test locally
  6. Submit to the registry

Module Structure

A DOCSight module is a directory with a manifest.json at its root:

my-module/
├── manifest.json          # Required — module metadata
├── __init__.py            # Required — Python package marker
├── routes.py              # Optional — Flask Blueprint (API endpoints / pages)
├── collector.py           # Optional — data collector class
├── storage.py             # Optional — SQLite storage helper
├── i18n/                  # Optional — translation files
│   ├── en.json
│   ├── de.json
│   ├── es.json
│   └── fr.json
├── static/                # Optional — CSS/JS assets
│   ├── style.css          # Auto-loaded if present
│   └── main.js            # Auto-loaded if present
└── templates/             # Optional — Jinja2 templates
    └── my_settings.html   # Settings panel template

Manifest Reference

Every module needs a manifest.json:

{
  "id": "community.mymodule",
  "name": "My Module",
  "description": "What this module does in one sentence",
  "version": "1.0.0",
  "author": "your-github-username",
  "minAppVersion": "2026.2",
  "type": "integration",
  "contributes": {
    "routes": "routes.py",
    "i18n": "i18n/"
  }
}

Required Fields

Field Type Description
id string Unique identifier. Must match ^[a-z][a-z0-9_.]+$. Use community. or your username as prefix.
name string Human-readable name shown in the UI
description string One-line description
version string Semantic version (major.minor.patch)
author string Your GitHub username
minAppVersion string Minimum DOCSight version (currently 2026.2)
type string One of: driver, integration, analysis, theme
contributes object What this module provides (see Contribution Types)

Optional Fields

Field Type Description
homepage string URL to module documentation or repo
license string SPDX license identifier (e.g., MIT)
config object Default configuration values (details)
config_secrets array Module-owned sensitive config keys (details)
hints object Driver connection form hints (details)
menu object Sidebar navigation entry (details)

Menu Entry

Add a sidebar link for your module:

"menu": {
  "label_key": "community.mymodule.nav_title",
  "icon": "puzzle",
  "order": 50
}
  • label_key — i18n key for the label (namespaced with your module ID)
  • iconLucide icon name
  • order — Sort position in sidebar (higher = lower; built-in modules use 10–30)

Config Defaults

Declare default values and they are automatically registered in DOCSight's config system:

{
  "config": {
    "mymodule_enabled": false,
    "mymodule_api_url": "",
    "mymodule_api_token": "",
    "mymodule_interval": 300
  },
  "config_secrets": [
    "mymodule_api_token"
  ]
}
  • Boolean values are auto-added to BOOL_KEYS (parsed from checkbox forms)
  • Integer values are auto-added to INT_KEYS (parsed from text inputs)
  • String values are stored as-is
  • Keys must not conflict with existing core config keys

Module-Owned Secrets

Use config_secrets for module passwords, API tokens, and other sensitive settings:

  • Every key in config_secrets must also exist in the same manifest's config object with a string default, normally ""; non-string defaults are rejected so encrypted values never enter scalar coercion.
  • config_secrets must be a list of unique strings.
  • Secret keys are encrypted at rest and masked in Settings after they are saved.
  • The collector config proxy exposes only the module's own declared secret keys and hides core secrets. This API boundary is not a Python sandbox; installed code must be trusted.
  • Settings templates should render secret fields as empty password/token inputs with data-config-secret="true". Add data-saved-secret="true" only when the masked config value indicates an existing saved secret. This explicit metadata makes an untouched field post the mask while an edited field posts the new value. Do not write saved secret values back into HTML value attributes.

The manifest capability contract is owned by DOCSight core rather than duplicated in this catalog. With sibling checkouts, run python3 ../docsight/app/manifest_contract.py .; catalog CI performs the same check against current core before running the registry-specific validator.


Contribution Types

The contributes object declares what your module provides. All keys are optional — include only what you need.

routes — API Endpoints & Pages

"contributes": { "routes": "routes.py" }

Your routes.py must export a Flask Blueprint named bp or blueprint:

from flask import Blueprint, jsonify

bp = Blueprint("mymodule_bp", __name__)

@bp.route("/api/mymodule/data")
def api_data():
    return jsonify({"status": "ok"})

The Blueprint is automatically registered with the Flask app. No URL prefix is added — you control the full path.

Accessing core services in routes:

from app.web import get_storage, get_config_manager

@bp.route("/api/mymodule/data")
def api_data():
    storage = get_storage()       # SQLite storage instance
    config = get_config_manager() # Config manager instance
    return jsonify({...})

driver: Modem Support

Community modem driver support is restored on main and in images built from this fix. The tagged v2026-09-16.1 release does not include this restoration.

Declare "type": "driver" and "contributes": {"driver": "driver.py:ExampleDriver"}. The entry point must be a plain module-local Python filename and a concrete ModemDriver subclass implementing login(), get_docsis_data(), get_device_info(), and get_connection_info(). Inherit the constructor (url, user, password) or provide a compatible one. Relative imports such as from .helper import parse_channels work within the module directory.

The manifest id is the modem selection key, for example community.example. IDs use lowercase letters, digits, dots and underscores, start with a letter and must fit the browser's 128-character driver-key limit. Use a unique community prefix for new modem support. To explicitly override a built-in driver, use its exact key as the module ID, for example fritzbox. No separate override field is needed. The enabled module supplies the implementation and displayed name for that key within its application; the global built-in catalog is unchanged. If hints is omitted or empty, the built-in hints are inherited. Nonempty module hints replace the complete built-in hint object rather than merging with it. Community constructors receive only (url, user, password), without built-in-specific keyword arguments. Only enabled modules whose complete contribution plan validates are registered. Restart after installing, enabling or disabling a module. The driver then appears in Settings > Extensions with a Community badge and in the setup/settings modem choices. Select it and save the connection settings. If an override is disabled, removed or rejected, a newly started application uses the original built-in implementation, name and hints for that key. If an unavailable module has its own key with no built-in fallback, connection tests and polling fail safely. The saved selection is retained in both cases.

Driver contributions cannot be combined with collectors or publishers, even under another module type. Themes cannot contribute drivers. These are contribution rules, not a sandbox: community modules execute trusted Python in the DOCSight process and drivers receive modem credentials. Install only code you trust; these rules do not prevent malicious code from accessing data or the network.

Optional hints can contain boolean needs_user, needs_password, username_required, credentials_required, and string or null default_url, default_user, url_hint, user_hint, password_hint. A nonempty default_url must be an HTTP(S) URL without embedded credentials. Invalid hints reject the module before they can break setup/settings initialization.

Copy TEMPLATE-DRIVER/ for a minimal, inert starting point. Threshold profiles, including VFKD thresholds, remain analysis modules.

collector — Scheduled Data Collection

"contributes": { "collector": "collector.py:MyCollector" }

Format: filename.py:ClassName. Your collector must extend the base class:

from app.collectors.base import Collector, CollectorResult

class MyCollector(Collector):
    name = "mymodule"

    def __init__(self, config_mgr, storage, web, poll_interval=300, **kwargs):
        super().__init__(poll_interval)
        self._config = config_mgr
        self._storage = storage  # core SnapshotStorage instance
        self._web = web          # web state manager

    def is_enabled(self):
        return self._config.get("mymodule_enabled", False)

    def collect(self):
        data = {"value": 42}
        return CollectorResult(source=self.name, data=data)

DOCSight passes config_mgr, storage, and web to every module collector. The base class provides exponential backoff on repeated failures (30s to 3600s max, auto-reset after 24h idle).

Note: Community modules receive a _ModuleConfigProxy instead of the raw ConfigManager. This proxy hides DOCSight core secret keys such as modem passwords and API tokens. Declare module-owned passwords or tokens in config_secrets; only those declared secret keys are exposed through that proxy. Keep saved secret values out of rendered HTML and handle them through dedicated password/token fields.

publisher — Data Export (e.g., MQTT)

"contributes": { "publisher": "publisher.py:MyPublisher" }

Format: filename.py:ClassName. Publisher classes receive collected data and export it to external services.

settings — Configuration UI

"contributes": { "settings": "templates/mymodule_settings.html" }

Your settings template is rendered in the Settings page under the Modules section. The panel ID must follow the pattern panel-mod-{module_id_with_dots_replaced_by_underscores}:

<div class="settings-panel" id="panel-mod-community_mymodule">
    <div class="settings-card glass">
        <div class="card-header">
            <div class="card-title-group">
                <div class="card-icon blue"><i data-lucide="puzzle"></i></div>
                <div>
                    <div class="card-title">{{ t.get('community.mymodule.title', 'My Module') }}</div>
                    <div class="card-subtitle">{{ t.get('community.mymodule.desc', 'Module description') }}</div>
                </div>
            </div>
        </div>
        <div class="form-grid cols-2">
            <div class="form-group">
                <label for="mymodule_api_url">{{ t.get('community.mymodule.api_url', 'API URL') }}</label>
                <input type="text" id="mymodule_api_url" name="mymodule_api_url"
                       value="{{ config.get('mymodule_api_url', '') }}"
                       placeholder="https://api.example.com">
            </div>
            <div class="form-group full-width">
                <label class="toggle">
                    <input type="checkbox" name="mymodule_enabled"
                           {{ 'checked' if config.get('mymodule_enabled') }}>
                    <span class="toggle-slider"></span>
                </label>
                <label style="margin-left:8px;">{{ t.get('community.mymodule.enable', 'Enable') }}</label>
            </div>
        </div>
    </div>
</div>

Important: Template filename must be unique. Do not name it settings.html (conflicts with core). Use mymodule_settings.html.

Available CSS classes:

  • settings-card glass - card container with glass effect
  • card-header, card-title-group, card-icon {color}, card-title, card-subtitle - card header
  • form-grid cols-2 - two-column form layout
  • form-group - single form field (label + input)
  • form-group full-width - full-width field spanning both columns
  • form-hint - hint text below an input
  • toggle, toggle-slider - toggle switch (replaces checkbox)

Icon colors: blue, purple, amber, green

i18n — Translations

"contributes": { "i18n": "i18n/" }

Place JSON files in the i18n/ directory:

i18n/
├── en.json    # English (required)
├── de.json    # German (recommended)
├── es.json    # Spanish (recommended)
└── fr.json    # French (recommended)

Keys are automatically namespaced with your module ID:

{
  "nav_title": "My Module",
  "api_url": "API URL"
}

Access in templates: {{ t['community.mymodule.nav_title'] }}

tab — Dashboard Tab

"contributes": { "tab": "templates/mymodule_tab.html" }

Adds a tab to the main dashboard view switcher.

card — Dashboard Card

"contributes": { "card": "templates/mymodule_card.html" }

Adds a card widget to the dashboard overview.

thresholds — Signal Threshold Profile

"contributes": { "thresholds": "thresholds.json" }

A threshold module provides regional signal quality thresholds for DOCSight's health assessment. Only one threshold profile can be active at a time — enabling a new one automatically disables the previous one.

Use the TEMPLATE-THRESHOLDS/ directory as a starting point.

Required Sections

The thresholds.json file must contain these three sections, each with a _default key:

Section Keys Format
downstream_power Modulation names (e.g., 256QAM, 4096QAM) { "good": [min, max], "warning": [min, max], "critical": [min, max] }
upstream_power Channel types: sc_qam, ofdma Same [min, max] array format
snr Modulation names { "good_min": N, "warning_min": N, "critical_min": N }

Optional Sections

Section Purpose
_meta Metadata (region, operator, docsis_variant, source, notes)
upstream_modulation QAM order thresholds (critical_max_qam, warning_max_qam)
errors Uncorrectable error rate (uncorrectable_pct: { warning: %, critical: % })

theme — Color Theme

"contributes": { "theme": "theme.json" }

A theme module provides dark and light color schemes. The theme.json must contain exactly two sections (dark and light), each with CSS custom property overrides:

{
  "meta": { "family": "dark-first" },
  "dark": {
    "--bg": "#1a1b26",
    "--surface": "#1f2335",
    "--text": "#c0caf5",
    "--accent": "#7aa2f7",
    "--good": "#9ece6a",
    "--warn": "#e0af68",
    "--crit": "#f7768e",
    "--muted": "#565f89"
  },
  "light": {
    "--bg": "#f0f0f0",
    "--surface": "#ffffff",
    "--text": "#1a1b26",
    "--accent": "#2e7de9"
  }
}

Only one theme can be active at a time. Users select themes in Settings > Appearance.

Security restriction: Theme modules cannot contribute collector, routes, publisher, or driver.

static — CSS & JavaScript

"contributes": { "static": "static/" }

Files are served at /modules/<module-id>/static/. Two files are auto-detected and loaded on every page:

  • style.css — stylesheet
  • main.js — JavaScript

Other static files (images, fonts, etc.) are accessible at their path but not auto-loaded.


Smart Capture Integration

Module collectors can integrate with DOCSight's Smart Capture engine to trigger automated measurements when module-specific events are detected.

Pattern: Your collector receives the Smart Capture engine via post-construction injection and evaluates events through it after saving them:

class MyCollector(Collector):
    name = "mymodule"

    def __init__(self, config_mgr, storage, web, **kwargs):
        super().__init__(300)
        self._storage = storage
        self._smart_capture = None

    def set_smart_capture(self, smart_capture):
        """Inject Smart Capture engine for event evaluation."""
        self._smart_capture = smart_capture

    def collect(self):
        events = self._detect_events()
        if events and hasattr(self._storage, "save_events_with_ids"):
            self._storage.save_events_with_ids(events)
            if self._smart_capture:
                self._smart_capture.evaluate(events)
        return CollectorResult(source=self.name)

Use save_events_with_ids() (not save_events()) so each event gets annotated with its database row ID for execution correlation.

The Smart Capture engine is wired to your collector in main.py after discover_collectors() returns. See the Connection Monitor module for a working example.


Testing Locally

  1. Mount the modules directory in your Docker setup:

    # docker-compose.yml
    services:
      docsight:
        image: ghcr.io/itsdnns/docsight:latest
        volumes:
          - docsight_data:/data
          - ./modules:/data/modules
        ports:
          - "8765:8765"
  2. Place your module in the modules/ directory:

    mkdir -p modules
    cp -r TEMPLATE modules/my-module
    # Edit modules/my-module/manifest.json and code
  3. Restart DOCSight to discover the new module:

    docker compose restart docsight
  4. Verify in Settings > Extensions. Your module should appear with a "Community" badge.

  5. Check logs for any loading errors:

    docker compose logs docsight | grep -i module

The standard persistent module path is /data/modules. An existing mount at /modules requires MODULES_DIR=/modules explicitly.

Error Handling

Contribution resolution failures reject the complete module plan while other modules continue loading. Installed Python code remains trusted:

  • Invalid manifests are skipped with a warning
  • Load failures are caught per-module and stored as error state
  • Broken modules show an error badge in Settings > Modules
  • Core functionality is never affected

Submitting a Module

Repository Model

The catalog supports two contribution styles:

  • Curated in-repo modules: Small, reviewed modules can live directly in this repository under their own directory.
  • External modules: Larger modules can stay in a contributor-owned GitHub repository and be referenced from registry.json.

In both cases, the registry entry must point to an installable module directory through download_url.

Prerequisites

  • Your module works locally with DOCSight
  • Your module has a README with setup, requirements, and support notes
  • Your manifest.json uses a unique ID that does not conflict with built-in DOCSight modules
  • Routes, collectors, settings forms, and stored configuration follow the safety checklist below

Steps

  1. Fork this repository

  2. Add or update your module files if the module lives in this catalog repository

  3. Add your module to registry.json:

    {
      "id": "community.mymodule",
      "name": "My Module",
      "description": "What it does in one sentence",
      "author": "your-github-username",
      "repo": "https://github.com/your-username/docsight-mymodule",
      "version": "1.0.0",
      "min_app_version": "2026.2",
      "type": "integration",
      "download_url": "https://api.github.com/repos/your-username/docsight-mymodule/contents/my-module?ref=main",
      "verified": false
    }
  4. Open a Pull Request

  5. We review: valid manifest, basic functionality, clear setup docs, and no malicious or unsafe behavior

  6. After merge, your module appears in the catalog

Verified Badge

Modules reviewed and tested by the DOCSight team receive "verified": true. Unverified modules are functional but marked accordingly in the catalog.

Pre-Submission Checklist

Before opening your PR, verify:

  • Auth on all routes — every @bp.route must have @require_auth (import from app.web)
  • No credentials in HTML — password/token fields must use value="", data-config-secret="true", and conditional data-saved-secret="true" metadata, never value="{{ config.password }}"
  • Escape dynamic content — any value from API responses inserted via innerHTML must be HTML-escaped; use textContent where possible
  • English API errors — error messages returned by API endpoints must be English (UI translations go in i18n files)
  • No exception details in responses — use generic error messages ("Connection failed"), log details server-side with logger.exception()
  • i18n parity — all language files (EN, DE, FR, ES) must have identical keys; include a template.json with empty values
  • Validate user input — config values used in URLs or paths must be sanitized (e.g. site names: alphanumeric only)
  • Trailing newlines — all JSON files must end with a newline
  • Version consistency — version string in manifest.json must match any version shown in docstrings or templates

Guidelines

  • One module per repository — keep it focused
  • Semantic versioning — update version in both your manifest and the registry entry
  • No docsight.* IDs — this prefix is reserved for built-in modules
  • English README required — additional languages welcome

Module Type Reference

Type Purpose Example
driver Modem support Minimal driver template, explicit same-ID built-in override
integration External service connection Ping test, uptime monitor, API bridge
analysis Data analysis/visualization Custom charts, reports, threshold profiles
theme UI customization Color schemes, layouts

Reference Implementations

These built-in DOCSight modules serve as examples:

Module Type Contributes Complexity
Reports analysis routes, i18n Minimal
Journal analysis routes, i18n Medium
Weather integration collector, routes, settings, i18n Full
Backup integration collector, routes, settings, i18n Full
Connection Monitor integration collector, routes, settings, i18n Full + Smart Capture
Speedtest integration collector, routes, settings, i18n Full
MQTT integration publisher, settings, i18n Publisher
VFKD Thresholds analysis thresholds Minimal
Classic Theme theme theme Minimal

License

MIT — same as DOCSight.

About

Community module catalog and development guide for DOCSight

Resources

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages