Create safe, powerful extensions for the ZDTT Terminal.
ZDTT supports a secure, sandboxed plugin system that allows developers to extend the terminal with custom commands.
Plugins live inside:
~/.zdtt/plugins/
A plugin is simply a Python file that:
-
Contains no top-level executable code
-
Defines functions or classes
-
Defines a mandatory function:
def register_commands(): return { "command": callable }
Plugins are automatically loaded when ZDTT starts or when you run:
plugins reload
ZDTT has strict safety validation to protect users from malicious plugins. Every plugin must pass six security layers:
At load time, ZDTT parses the plugin using Python’s AST.
Allowed at the top level:
- Imports
- From-imports
- Function definitions
- Class definitions
- A file-level docstring
Forbidden (causes quarantine):
- Print statements
- Assignments
- Function calls
- Loops
- Try/except blocks
- Any executable code
- Anything that runs automatically
Example of an unsafe plugin:
print("hacked!") # ❌ this will be quarantined
os.system("rm -rf /") # ❌ also quarantinedUnsafe plugins are moved to:
~/.zdtt/quarantine/
Plugins may import modules:
import os
import subprocessHowever: Any plugin with imports triggers a trust confirmation.
If the user does not trust the plugin, it is quarantined.
If the user approves, the plugin name is added to:
config.json → trusted_plugins
Only trusted plugins may use __import__.
Plugins run inside a restricted environment:
- Only safe builtins are exposed
- Dangerous builtins (like
execoreval) are blocked - Imports only work for trusted plugins
- Code cannot escape the sandbox
Every plugin must define:
def register_commands():
return {"name": function}ZDTT verifies:
- The function exists
- The return value is a dict
- All values are callable
- Command names are valid strings
Plugins cannot override important commands such as:
ssh, sudo, su, cp, mv, rm, ls, cat,
chmod, chown, history, zps, zdtt,
pip, python, python3, curl, wget
Attempting to override them causes quarantine.
Once the plugin passes all previous checks, ZDTT adds its commands to the shell environment.
Example:
{
"weather": cmd_weather,
"hello": cmd_hello
}These appear in autocomplete and help.
A minimal valid plugin:
"""
My Plugin
"""
def cmd_test(args):
print("Hello from ZDTT plugin!")
def register_commands():
return {"test": cmd_test}ZDTT passes command arguments as a list.
Example:
hello world how are you
Results in:
cmd_hello(["world", "how", "are", "you"])From example_plugin.py:
"""
Example ZDTT Plugin
"""
import subprocess
import os
def cmd_hello(args):
...
def cmd_weather(args):
...
def cmd_sysinfo(args):
...
def register_commands():
return {
"hello": cmd_hello,
"weather": cmd_weather,
"sysinfo": cmd_sysinfo
}All three commands use argument lists exactly as ZDTT passes them.
Place plugin file in:
~/.zdtt/plugins/
Then reload:
plugins reload
Install directly from a URL:
zps install https://raw.githubusercontent.com/user/repo/plugin.py
ZPS will:
- Download the file
- Save it to your plugin directory
- Warn if it already exists
- Ask if you trust imports (if any)
ZDTT logs plugin errors to:
~/.zdtt/plugin_errors.log
Quarantined plugins appear in:
~/.zdtt/quarantine/
Reload plugins:
plugins reload
✔ Wrap all code inside functions ✔ Avoid imports unless required ✔ Validate arg lists ✔ Use try/except around risky operations ✔ Keep commands short and simple ✔ Document your commands ✔ Never modify global state ✔ Don’t override protected commands
def cmd_calc(args):
"""Usage: calc <expression>"""
...ZDTT automatically pulls these docstrings into the help system.
expr = " ".join(args)try:
subprocess.run(["ls"])
except Exception as e:
print("Error:", e)Use try/except to avoid crashing the shell.
"""
Plugin Name: <your plugin>
Description: <what it does>
Author: <you>
"""
# Optional imports (will require trust prompt)
import subprocess
def cmd_example(args):
print("Example plugin working!")
def register_commands():
return {
"example": cmd_example
}