Skip to content

CivilKit extensions (.ckext modules)

CivilKit can be extended by sandboxed modules that add new design checks, load generators, importers and report sections. A module is distributed as a .ckext file - a zip bundle containing a manifest, source, and cited tests - which installs directly in the browser with no server or build step.

The current modules run as MicroPython inside the host's sandboxed runtime. They declare a form, read from the host's trusted capability bridge, and return structured results plus calc-sheet working. Future modules will also compile from Nim or Rust to WebAssembly using the same bundle shape.

What a .ckext bundle contains

my-check.ckext
├── manifest.json            # identity, capabilities, contribution points
├── main.py                  # MicroPython source (current modules)
└── tests/
    └── worked-examples.json # cited expected results for the trust badge

WASM modules replace main.py with main.wasm. main.js is reserved for first-party, trusted extensions only - untrusted/community modules must be Python or WASM so they cannot touch the DOM or network directly.

manifest.json

json
{
  "id": "com.example.member-shear-check",
  "name": "Member shear check",
  "version": "1.0.0",
  "description": "AS 4100 Clause 5.11 member shear capacity check.",
  "author": { "name": "Example Engineering", "url": "https://example.com" },
  "license": "MIT",
  "homepage": "https://example.com/ckext/member-shear-check",
  "keywords": ["AS 4100", "shear", "steel"],
  "civilkitApi": "^1.0",
  "contributes": [
    {
      "point": "design-check",
      "id": "member-shear-check",
      "label": "Member shear check"
    }
  ],
  "capabilities": ["getModel", "getSectionProps"],
  "tests": "tests/worked-examples.json"
}

Required fields

FieldPurpose
idReverse-DNS identifier, e.g. com.example.my-check
nameDisplay name in menus and the store
versionSemver, e.g. 1.0.0
civilkitApiHost compatibility range, e.g. ^1.0
contributesArray of contribution points (currently design-check)

Display and trust fields

FieldPurpose
descriptionShort subtitle shown on the store card
authorName or { name, email, url } object
licenseSPDX id such as MIT, Apache-2.0, UNLICENSED
homepageLink to docs or the module's project page
keywordsTags for store search
capabilitiesHost callbacks the module may call (least-privilege)
testsPath to worked-example tests that drive the trust badge

author, homepage, repository and license are self-declared. They are shown to the user so a human can judge provenance, but they confer no trust. Trust comes only from the test gate (the Verified badge, below).

A simple Python module

Every MicroPython design-check module exposes two functions:

python
import json, math, civilkit

def build_ui(inputs, result, calcLines):
    return {
        "type": "panel",
        "title": "Member shear check",
        "children": [
            {
                "type": "field",
                "id": "member_id",
                "label": "Member",
                "inputType": "enum",
                "options": [{"value": "m1", "label": "m1"}],
                "default": inputs.get("member_id", "m1")
            },
            {
                "type": "field",
                "id": "v_star",
                "label": "Design shear V* (N)",
                "inputType": "number",
                "default": inputs.get("v_star", 100000)
            },
            {"type": "button", "id": "run", "label": "Run check", "action": "run-check"}
        ]
    }

def check(inputs):
    model = json.loads(civilkit.getModel())
    member_id = inputs.get("member_id", "")
    props = json.loads(civilkit.getSectionProps("250UB25.7"))
    fy = props.get("fy_MPa", 300)
    d = props.get("d_mm", 248)
    tw = props.get("tw_mm", 5)
    aw = d * tw
    phi = 0.9
    phi_vv = phi * 0.6 * fy * aw / 1000.0
    v_star = float(inputs.get("v_star", 0)) / 1000.0
    util = v_star / phi_vv if phi_vv > 0 else 999
    status = "OK" if util <= 1.0 else "OVER"

    return {
        "result": [
            {"label": "phiVv", "value": round(phi_vv, 2), "unit": "kN"},
            {"label": "Utilisation", "value": round(util, 3)},
            {"label": "Status", "value": status, "status": "ok" if status == "OK" else "fail"}
        ],
        "calcLines": [
            {
                "standard": "AS 4100",
                "year": "1998",
                "clause": "5.11",
                "label": "Member shear capacity",
                "eq": "phiVv = phi * 0.6 * fy * Aw = %.2f kN" % round(phi_vv, 2),
                "tex": r"\phi V_v = \phi \cdot 0.6 \cdot f_y \cdot A_w"
            }
        ]
    }

The host renders the UI tree returned by build_ui, calls check when the user clicks the run button, and renders the result list and calcLines working.

Capability bridge

The module calls the host through a least-privilege bridge. Declare only the capabilities you need:

CapabilityReturns
civilkit.getModel()The current model as a JSON string
civilkit.getSelection()The currently selected object, if any
civilkit.getSectionProps(name)Section geometry and analysis properties
civilkit.memberCapacity(input)AS 4100 member capacities from the engine
civilkit.log(msg)Sandbox-safe logging to the host console

The module cannot call anything it did not declare, and it never touches the DOM directly.

Worked-example tests

A Verified module ships one or more cited worked examples. The host runs them on install and badges the module only if every example passes.

json
[
  {
    "id": "m1-100kN",
    "input": {
      "member_id": "m1",
      "v_star": 100000,
      "phi": 0.9
    },
    "expected": {
      "result": [
        {"label": "phiVv", "value": 200.88, "unit": "kN"},
        {"label": "Utilisation", "value": 0.498},
        {"label": "Status", "value": "OK"}
      ]
    },
    "source": "AS 4100-1998 Cl 5.11 hand calc: 0.9 * 0.6 * 300 MPa * 248 mm * 5 mm / 1000 = 200.88 kN."
  }
]

The installer compares the module's output against expected.result and labels the module:

  • Verified - all bundled tests pass against cited worked examples.
  • Community - installed, but tests are absent or not independently reviewed.

Installing and using a module

In the current experiments/py-module/demo.html host:

  1. Open the Modules tab.
  2. Drop a .ckext file onto the drop zone, or click Install on a module in the store.
  3. The host validates the manifest, runs the worked-example tests, and badges it.
  4. The module appears under the Custom menu. Click it to open the check.

Modules are persisted in the browser's IndexedDB; they survive a page refresh. Use Uninstall to remove one.

Bundling your own module

  1. Create a directory with manifest.json, main.py, and tests/worked-examples.json.
  2. Add the module to experiments/py-module/store/index.json if you want it in the local store.
  3. Zip the contents from inside the directory:
bash
cd my-module
zip -r ../my-module.ckext manifest.json main.py tests
  1. Load the demo, install the .ckext, and run the check.

What's next for extensions

The current demo proves design-check Python modules. The same .ckext bundle shape will later support:

  • importers - custom file formats returning a model patch
  • load generators - wind, seismic, moving-load trains
  • report sections - stamped calc-report contributions
  • viewport overlays - host-drawn diagrams from module draw commands
  • WASM modules - Nim/Rust/AssemblyScript compute cores

For the full contract, see docs/architecture/extension-system-spec.md in the repository.