Install
openclaw skills install @zkeviny/seal-and-license-workflow-skillsSeal skill packages and workflow folders so they execute only on authorized nodes — no read, no modify, no resell. 100% local, single‑node bound, zero‑exposure IP protection powered by MGC Blackbox 1.5.0+.
openclaw skills install @zkeviny/seal-and-license-workflow-skillsSeal Python small-systems / workflows into local packages — one AES key per node, no source code readable, no cloud, no exposure.
| # | Scenario | Pain point |
|---|---|---|
| 1 | Selling scripts / small tools — Independent developer writes a data-processing / report / scraper script, wants to sell 100 copies | Fear of unlimited copying and reselling; a regular zip means gifting source |
| 2 | Delivering internal small systems — Vendor hands a small system built from scripts to a client | Client gets the source and may modify it or hand it to a third party |
| 3 | Delivering enterprise automation — Automation consultant packages a workflow for an enterprise | Each enterprise needs its own license; keys cannot be shared |
| 4 | Paid knowledge products — Selling an automation method as a content product | Let users run it, but cannot leak the core method |
Common requirement: delivery = authorization, and it must be one-key-per-node, one-node-per-key.
Write your workflow as you would normally:
from helpers.fetch import get_weatherimport configPath(__file__).parent / "settings.json"subprocess.run([sys.executable, "helper.py"])MGC 1.5.2's ext08 automatically recognizes these import / relative-path / subprocess relationships. At runtime, execution is routed through sealed execution:
cat the source directlyCompared with traditional approaches: traditional schemes require you to rewrite scripts as "call by info_owner", turning 5 lines of calls into 50 lines. MGC 1.5.2 takes over for you.
Each customer receives an independent AES key:
Author node Customer node
─────────── ────────────
mgc_save_file(path="./workflow")
mgc_seal_package(ext04=customer_pub)
│
▼
encrypted package (.mgc_file)
│
└──── any channel ─────► mgc_save_file(path="./received.mgc_file")
mgc_run(...)
| # | User pain | MGC 1.5.2 solution |
|---|---|---|
| P1 | Customer can directly cat my .py files | Full AES-256 encryption, no plaintext on disk |
| P2 | Customer copies scripts to a colleague | AES key bound to their node RSA — a different node cannot decrypt |
| P3 | Multiple scripts use import; customer's machine changes paths | ext08 auto-recognizes import / Path / subprocess, runtime sealed routing, no path dependency |
| P4 | Customer modifies my code and stops paying | AES signature verification, any modification immediately invalidates |
| P5 | I have to maintain a "runbook"; customers can't set up dependencies | ext05 auto-infers dependency list from imports, ext06 infers platforms |
| P6 | Re-sealing for each customer is tedious | mgc_seal_package handles folder-level sealing + node binding in one call |
pip install mgc-blackboxmgc (API at http://127.0.0.1:57219, WebUI at http://127.0.0.1:57218)my_workflow/
├── manifest.json
├── SKILL.md
├── run.py
├── helpers/
│ ├── fetch.py
│ └── parse.py
└── config/
└── settings.json
mgc_save_file(
path="./my_workflow",
info_owner="my_workflow",
diff_2="my_workflow",
)
This step automatically:
argparse and writes ext02ext05 (dependency list)ext06 (platform list)ext08 (cross-script call graph)# Customer runs this on their own machine (WebUI or MCP)
node_pub = mgc_get(
info_type="__NODE_PUB__",
info_owner="__NODE_PUB__",
)
# Customer sends you the multi-line PEM (public key is not secret; any channel works)
WebUI path (1.4.7+ recommended): WebUI → Skill page → Settings dropdown → Node Public Key → copy multi-line PEM.
⚠️
ext04must contain real\nnewlines; do not concatenate into a single line.
mgc_seal_package(
info_owner="my_workflow",
diff_2="my_workflow",
ext04=node_pub, # customer's PEM
)
# → generates .mgc_file
Send the .mgc_file to the customer through any channel.
mgc_save_file(path="./received.mgc_file")
# MGC decrypts the package into local records
# List package entries (metadata only, no plaintext)
mgc_find(info_owner="my_workflow", diff_2="my_workflow")
# Read manifest (plaintext, explains the workflow structure)
mgc_get(info_type="file", info_owner="my_workflow", diff_2="my_workflow")
# Read SKILL.md (plaintext, follow the instructions to call scripts)
mgc_get(info_type="file", info_owner="my_workflow", diff_2="my_workflow", diff_3="SKILL.md")
mgc_run(info_owner="my_workflow", diff_2="my_workflow")
# → {"pid": 12345, "status": "started"}
# AI only gets the launch status, never the source
Scripts inside the same folder package can freely use import / relative paths / subprocess** — MGC 1.5.2's ext08 automatically recognizes and routes them through sealed execution at runtime. No need to rewrite as API calls.
Only cross-folder-package calls (different info_owner + diff_2) require the MGC API:
import os, requests
with open(os.path.expanduser("~/.mgc/database/mgc_black_box/.mgc_token")) as f:
MGC_TOKEN = f.read().strip()
def call_remote_script(info_owner, ext02=None):
return requests.post(
"http://127.0.0.1:57219/api/mgc/sensitive/get",
headers={"X-MGC-Token": MGC_TOKEN},
json={
"info_type": "script",
"info_owner": info_owner,
"diff_1": info_owner,
"action": "run",
"ext01": "python",
"ext02": ext02,
},
).json()
The author's SKILL.md must be plaintext — it is the routing instruction for the customer: which script to call in which scenario, in what order, with what parameters. Without it, the customer cannot use the package at all.
This is the public API contract, not implementation detail. Core logic must go in the scripts — see the next rule.
Both SKILL.md and prompts are plaintext — the customer can read them. Therefore:
| ✅ Do this | ❌ Don't do this |
|---|---|
Put core algorithm in run.py | Put core algorithm in SKILL.md |
| SKILL.md says "when to call run.py, what args to pass" | SKILL.md says "the full business flow is…" (with details) |
| Business rules, thresholds, formulas → in scripts | Business rules written in prompts |
The prompt is the customer-visible "manual". The algorithm is what you're selling — it must be locked inside the scripts.
Source: MGC skill_spec §2.4. When you save a folder via
mgc_save_file, MGC statically parses every script to build the cross-script call graph (ext08). At runtime, MGC intercepts these patterns and routes them through sealed execution — no files on disk, no plaintext, no rewrites.
| Pattern | Code example | ext08 edge kind | Recognized? |
|---|---|---|---|
from import | from config import DEFAULT_CITY | from_import | Yes |
from import with alias | from helpers.fetch import get_weather as gw | from_import (asname preserved) | Yes |
Plain import | import helpers.parse | import | Yes |
Relative path via Path | Path(__file__).parent / "config.py" | path | Yes |
Relative path via open() | open("config.json") against sibling file | path | Yes (recognition only in the current version; runtime routing is not yet supported) |
| Subprocess with relative path | subprocess.run([sys.executable, str(Path(__file__).parent / "config.py"), ...]) | path (recognized via the Path(...) literal above; runtime also intercepts the subprocess call) | Yes — only when the caller and target were saved into MGC together as one workflow package. |
| Subprocess with literal list | subprocess.run(['helper.py', '--x']) (or from subprocess import run; run([...])) | subprocess (callee field records which subprocess function: run / Popen / call / check_call / check_output) | Yes — same package rule as above. Runtime interception runs the helper as sealed subprocess. |
| Dynamic import | importlib.import_module(module_name) | — | No — MGC statically parses only string literals; dynamic names are not recognized. At runtime, Python's standard import will attempt to resolve the name (typically fails with ModuleNotFoundError). |
| Absolute path | open("/Users/alice/scripts/config.py") | — | No — absolute paths are not portable and cannot be sealed. Use MGC location addressing instead. |
| Cross-subdirectory relative path | subprocess.run([list, "../y.py"]) or Path(__file__).parent.parent / "y.py" when .. resolves outside the caller's directory | — | No — see Scope below. |
| Function-level call without import | A function defined in another file called only by reference (no import / from-import / literal Path referencing it) | — | No — the parser only inspects string literals reachable via the conventional forms above. |
Scope (1.5.2):
info_owner + diff_2 of the caller and the callee must be identical).pkg_root/CROSS-FOLDER/x.py → ../y.py) — not recognized. The parser treats .. as out-of-scope, so ext08 will not contain the edge. Place helpers and drivers in one directory.import / from-import / Path literals in source — not recognized.Key invariants:
argparse on each script is independent — the driver's args go to the driver's ext02, each helper's args go to the helper's own ext02.Path(__file__).parent / "x.py" resolves correctly on macOS, Linux (POSIX), and Windows (\ separator) — MGC normalizes via _normalize_path in subprocess_adapter.capture_output=True, text=True works because the launched helper is a real Python interpreter with UTF-8 decoding.Limits. The cross-script call only works when both scripts are saved together. To orchestrate scripts across different packages, use the REST API pattern — pass the MGC location (info_owner) of the target script.
| Code | Meaning | Resolution |
|---|---|---|
DIFF2_MISMATCH | Script used from-import but target script is not in the same folder package | Put both scripts into the same mgc_save_file package; or use MGC API |
DEP_MISSING | A dependency in ext05 is not installed on the customer's machine | pip install <missing dep> (MGC does NOT auto-install) |
PLATFORM_INCOMPAT | The ext06 platform list doesn't include the current OS | Re-save on a supported platform, or extend platform branches in code |
args_not_recognized | argparse parsing failed | Check add_argument usage; ensure literal default= |
dynamic_args_detected | Default value uses dynamic computation (e.g. datetime.now()) | Use literal defaults, or pass via ext02 explicitly |
Invalid PEM format | ext04 is not multi-line PEM | Copy from mgc_get(info_type='__NODE_PUB__') verbatim, preserving \n |
| ✅ Good fit | ❌ Not a good fit |
|---|---|
| Selling or licensing Python small tools | Open-source scripts (use GitHub) |
| Delivering internal small systems to clients | Large SaaS products (use cloud solutions) |
| Cross-node execution of automation workflows | Single-machine personal scripts (no need to seal) |
| "Tool packs" in paid knowledge products | Customer machines with full source environment for re-development |
| Dimension | GitHub private repo | SaaS encryption service | License server | MGC Sealed Delivery |
|---|---|---|---|---|
| Requires cloud | Yes | Yes | Yes | No |
| Monthly cost | Medium | High | Medium | None |
| Prevents source leak | ❌ Private but visible | ⚠️ Service-dependent | ❌ Server-side visible | ✅ Completely invisible |
| Prevents resell | ❌ | ⚠️ Service-dependent | ⚠️ Service-dependent | ✅ Node-locked, cannot resell |
| Offline delivery | ❌ | ❌ | ❌ | ✅ Any channel |
| Good for small systems | ⚠️ | ❌ Heavy | ⚠️ Heavy | ✅ Light |
| Tool | Purpose | Required params |
|---|---|---|
mgc_save_file | Store a local folder (auto-recognizes workflow) | path, info_owner |
mgc_seal_package | Seal a package for target node | info_owner, ext04 (PEM), output_dir |
mgc_save | Store a single script (not in a package) | info_type, info_owner, content |
mgc_seal | Seal a single script | info_owner, ext04 (PEM) |
mgc_run | Black-box execution | info_type="script", info_owner, diff_1 |
mgc_get | Retrieve value / retrieve node_pub | info_type, info_owner |
mgc_find | Fuzzy search | info_owner, match_mode |
mgc_list | List all metadata | — |
mgc_package | Plaintext packaging within same trust zone | info_owner |
mgc_open_webui | Open WebUI | — |