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+&lt; 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?

  1. <application_date> and <processing_days> are replaced with the current field values before execution.
  2. datetime is available inside the sandbox; the code calculates application date + 30 days.
  3. The last expression (f"…") is the result – no extra boilerplate needed.
  4. 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:

  1. Open the SSP-statistics qFORM.
  2. Pick the SSP file via the 📁 button.
  3. mjEdit replaces <ssp_path> with the chosen path and runs the QC script.
  4. 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:

  1. Automatic backups – compressed (GZIP), time-based, on open and on save
  2. Version archive – named snapshots with diff view and preview

Before any restore an automatic backup of the current file is created.