Skip to content

Python Backend ​

Extensions can include an optional Python backend for work that should not run in the browser. Backend code can be invoked from a panel with window.mantis.backend.invoke(action, payload) or from the extension host with mantis.backend.invoke(action, payload).

Manifest declaration ​

json
{
  "permissions": ["backend:invoke"],
  "backend": {
    "runtime": "python",
    "entry": "backend/main.py",
    "requirements": "humanize==4.10.0\n",
    "network": false,
    "actions": ["ping", "dependencyCheck"]
  }
}

The backend declaration does not expose backend source files to the frontend after install.

Entry file ​

The backend entry file is loaded as a Python module. Mantis invokes either:

  • invoke(action, payload, ctx) if the module defines invoke
  • a function named after the action, called as function(payload, ctx)

Function-per-action style ​

python
def ping(payload, ctx):
    return {
        "message": "pong",
        "payload": payload,
        "extension_id": ctx.get("extension_id"),
    }

Browser code:

js
const result = await window.mantis.backend.invoke('ping', {
  sentAt: new Date().toISOString(),
});

Single dispatcher style ​

python
def invoke(action, payload, ctx):
    if action == "ping":
        return {"message": "pong", "payload": payload}

    raise ValueError(f"Unknown action: {action}")

Use this style when actions share setup code.

Context object ​

The ctx object includes:

FieldDescription
actionRequested backend action.
payloadPayload passed by the panel.
project_idCurrent Mantis project/space id.
user_idCurrent user id.
extension_idExtension id from the manifest.
extension_versionExtension version from the manifest.

Treat ctx as metadata. Do not use it as a secret store.

Dependencies ​

Use requirements for pip dependencies:

json
{
  "backend": {
    "runtime": "python",
    "entry": "backend/main.py",
    "requirements": "humanize==4.10.0\n",
    "actions": ["dependencyCheck"]
  }
}

Example backend:

python
import humanize


def dependencyCheck(payload, ctx):
    count = int(payload.get("count", 1234567))
    return {
        "dependency": "humanize",
        "humanized": humanize.intword(count),
    }

Mantis caches installed dependencies by extension id, version, project, user, and requirements hash. If requirements change, the cache is cleared and reinstalled.

Network access ​

Python backends run without network access by default:

json
{
  "network": false
}

Set network to true only when the extension genuinely needs external network access:

json
{
  "network": true
}

Network access is shown in the install trust dialog.

Timeouts ​

Backend dependency installation and action execution are time-limited. Long-running actions should return clear progress/state through your own backend design instead of blocking indefinitely.

Return values ​

Backend actions must return JSON-serializable data. Mantis serializes the result and returns it to the host or panel caller.

Good:

python
return {
    "count": 3,
    "labels": ["a", "b", "c"],
}

Avoid returning open files, raw class instances, or other non-serializable objects.

Error handling ​

Raise normal Python exceptions for invalid input:

python
def summarize(payload, ctx):
    point_ids = payload.get("pointIds")
    if not isinstance(point_ids, list):
        raise ValueError("pointIds must be a list")

    return {"count": len(point_ids)}

The host or panel receives the error as a rejected promise.

js
try {
  await window.mantis.backend.invoke('summarize', {});
} catch (error) {
  console.error(error.message);
}

Security notes ​

Backend code is trusted code. Mantis validates paths, applies timeouts, and defaults to no network, but extension authors should still:

  • validate payload shapes
  • avoid logging sensitive user data
  • avoid silent failures
  • keep dependency lists small and pinned
  • avoid enabling network unless required