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
{
"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 definesinvoke- a function named after the action, called as
function(payload, ctx)
Function-per-action style
def ping(payload, ctx):
return {
"message": "pong",
"payload": payload,
"extension_id": ctx.get("extension_id"),
}Browser code:
const result = await window.mantis.backend.invoke('ping', {
sentAt: new Date().toISOString(),
});Single dispatcher style
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:
| Field | Description |
|---|---|
action | Requested backend action. |
payload | Payload passed by the panel. |
project_id | Current Mantis project/space id. |
user_id | Current user id. |
extension_id | Extension id from the manifest. |
extension_version | Extension version from the manifest. |
Treat ctx as metadata. Do not use it as a secret store.
Dependencies
Use requirements for pip dependencies:
{
"backend": {
"runtime": "python",
"entry": "backend/main.py",
"requirements": "humanize==4.10.0\n",
"actions": ["dependencyCheck"]
}
}Example backend:
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:
{
"network": false
}Set network to true only when the extension genuinely needs external network access:
{
"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:
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:
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.
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