mjEdit organises work in a multi-tab system. Every tab is specialised for a particular workspace and works seamlessly with the others.
Text tab — the JSON editor
The heart of mjEdit: a fully-featured JSON editor with syntax highlighting, real-time validation, bracket matching and intelligent auto-repair.
Performance modes for large files
| File size | Behaviour |
|---|---|
| < 100 KB | Full highlighting + spell check + all features |
| 100 KB – 200 KB | Simplified highlighting (keys/brackets only), no spell check |
| > 1 MB | No highlighting, lazy loading (first 10,000 lines) |
Important shortcuts
| Shortcut | Action |
|---|---|
| Ctrl+Alt+J | Format JSON (pretty-print) |
| Ctrl+F / Ctrl+H | Search / search & replace |
| F3 | Next match |
| Ctrl+< | Insert snippet |
Form tab — qFORM form system & QC scripts
The form tab automatically turns JSON data into editable forms. The proprietary qFORM format combines structured data entry with embedded Python scripts (QC scripts) running in a RestrictedPython sandbox.
Automatic widget detection
| Field type | Widget | JSON detection |
|---|---|---|
| Nested object | QGroupBox | Value is {} (dict) |
| Array / list | QGroupBox (orange) | Value is [] (array) |
| QC script | QTextEdit (readonly) | Value starts with "QC", >10 chars |
| Checkbox | QCheckBox | Value is true or false |
| Dropdown | QComboBox | Value starts with |, ≥2 pipes |
| File path | QLineEdit + 📁 | Value matches a path pattern |
| Date | QLineEdit + DATE | Date keyword in key or date format in value |
| Text (default) | AutoResizeTextEdit | Anything else |
QC script syntax in a nutshell
QC scripts are embedded inside string fields of a qFORM file, compiled into a RestrictedPython sandbox and executed during rendering. The last expression result replaces the code block in the form.
| Element | Meaning |
|---|---|
@( … )@ |
Marks a QC code block (multiple blocks per field are allowed) |
<fieldname> |
Placeholder – replaced before execution by the value of a qFORM field (dot-notation supported for nested paths) |
json_data |
The whole qFORM file as a dict – e.g. when an OSCAL file is opened directly in the form tab |
| Allowed modules | datetime, json, math, re, time, socket, urllib, optionally requests (no direct file access) |
Example 1 — Beginner: pickup date for a new passport
A government qFORM captures the application date and automatically derives the earliest pickup date (classic: application date + 30 days).
{
"Passport application": {
"application_date": "DATE 06.05.2026",
"processing_days": 30,
"pickup_date": "QC: computed pickup: @(\nfrom datetime import datetime, timedelta\napp = datetime.strptime('<application_date>', '%d.%m.%Y')\npickup = app + timedelta(days=int('<processing_days>'))\nf'{pickup.strftime(\"%A, %d.%m.%Y\")} (earliest)'\n)@"
}
}
The QC field is then rendered for example as:
computed pickup: Friday, 05.06.2026 (earliest)
What happens here?
<application_date>and<processing_days>are replaced with the current field values before execution.datetimeis available inside the sandbox; the code calculates application date + 30 days.- The last expression (
f"…") is the result – no extra boilerplate needed. - As soon as the case worker changes
application_date, mjEdit refreshes the display automatically.
Example 2 — Advanced: counting SSP implementation status
A pure OSCAL SSP file does not contain a QC field. There is, however, a way to use a temporary QC field to evaluate the data and combine OSCAL files with qFORM sensibly — either directly in the form tab or via a standalone qFORM with a file picker (see below).
Variant A — open the SSP directly in the form tab
When you open an OSCAL SSP JSON in mjEdit and view it in the form tab, the SSP itself is json_data. A QC block placed temporarily into a free description field can evaluate the entire document — useful for ad-hoc statistics during a review:
@(
reqs = json_data.get('system-security-plan', {}).get('control-implementation', {}).get('implemented-requirements', [])
implemented = sum(
1 for r in reqs
for s in r.get('statements', [])
for bc in s.get('by-components', [])
if bc.get('implementation-status', {}).get('state') == 'implemented'
)
total = len(reqs)
f"Implemented: {implemented} of {total} requirements ({(implemented/total*100 if total else 0):.1f} %)"
)@
Pro: no extra tooling, immediately usable. Con: the QC block must be removed before saving if the file is to remain schema-valid.
Variant B — dedicated “SSP statistics” qFORM with a file picker
The clean variant is a standalone qFORM that holds a file path to the SSP and reads it via urllib. The file-path widget (📁 button) is natively supported by qFORM; the QC script reads the value through <ssp_path>:
{
"SSP statistics": {
"ssp_path": "C:/oscal/ssp_acme.json",
"report": "QC report: @(\nimport urllib.request, json\nfrom pathlib import PurePath\nuri = 'file:///' + str(PurePath('<ssp_path>')).replace('\\\\','/')\nwith urllib.request.urlopen(uri) as f:\n ssp = json.load(f)\nreqs = ssp.get('system-security-plan', {}).get('control-implementation', {}).get('implemented-requirements', [])\nimpl = sum(1 for r in reqs for s in r.get('statements', []) for bc in s.get('by-components', []) if bc.get('implementation-status', {}).get('state') == 'implemented')\nf'{impl} of {len(reqs)} controls implemented'\n)@"
}
}
User steps:
- Open the SSP-statistics qFORM.
- Pick the SSP file via the 📁 button.
- mjEdit replaces
<ssp_path>with the chosen path and runs the QC script. - The result appears live in the form — every path change triggers a recalculation.
Pro: reusable, the SSP file itself stays untouched, the QC script can produce arbitrarily complex output (traffic-light per control family, missing components, POA&M cross-references, etc.).
Markdown tab — documentation and reports
A fully-featured Markdown editor with live preview in split view, GitHub Flavored Markdown, auto-formatting via mdformat and PDF/HTML export.
Extended task list system
| Element | Syntax | Example |
|---|---|---|
| Checkbox | - [ ] / - [x] |
- [x] Server configured |
| Priority | Keywords | critical, important, high |
| Status | Keywords | in progress, paused, complete |
| Assignment | @person |
@miller |
| Date | 📅 YYYY-MM-DD |
📅 2026-04-30 |
| Tags | #tag |
#security #patch |
| OSCAL links | &OSCAL|...|... |
Direct link to OSCAL controls |
| POA&M references | &POAM|...|... |
Direct link to remediation items |
PDF tab — viewer, annotator and redaction tool
PDF documents are displayed directly in mjEdit – with multi-tab support, annotations, full-text search and secure redaction.
- Zoom 25 % – 400 %, fit-to-width / fit-to-height
- Highlighter, notes, freehand drawing, shapes, text insertion
- Secure redaction with Unicode block character (■): content is actually removed, not just covered
- Save the PDF including all annotations as a new file
Browser tab — integrated web browser
A fully-featured Chromium-based browser inside mjEdit – with a two-tier bookmark system.
Global vs. private bookmarks
| Property | Global bookmarks | Private bookmarks |
|---|---|---|
| Purpose | Team resources, shared references | Personal collection |
| Location | data/global-browser-bookmarks/ |
config/ |
| Add | Dialog with title, tags, initials | One click – no dialog |
| Team sharing | Shareable via Git | Local only |
Save web pages as OSCAL back-matter resources
Every page on display can be inserted into an open OSCAL document as a back-matter resource via right-click – one click instead of copy-pasting between applications.
OSCAL tabs (plugin) — specialised editors
The OSCAL tabs open automatically when mjEdit detects an OSCAL file. For each of the 8 OSCAL document types there is a tailored editor – see OSCAL integration.
File tree view — the central cockpit
- Lazy loading for large directories (1,000+ files)
- Coloured file icons per extension (configurable)
- Real-time filter, case-insensitive and recursive
- Context menu with type-specific actions
- Backup indicator showing existing backups
- Multi-selection for batch operations
- “Open as text” bypasses automatic OSCAL routing
Backup and versioning concept
mjEdit provides a two-tier safety net:
- Automatic backups – compressed (GZIP), time-based, on open and on save
- Version archive – named snapshots with diff view and preview
Before any restore an automatic backup of the current file is created.