mjEdit organizes work in a multi-tab system. Each tab is specialized for a specific workspace and works seamlessly with the others.

Text Tab — The JSON editor

The heart of mjEdit: a full-fledged JSON editor with syntax highlighting, real-time validation, bracket matching and intelligent auto-repair.

Performance modes for large files

File size behavior
< 100KB Full highlighting + spell check + all features
100KB – 200KB Simplified highlighting (keys/brackets only), no spell check
> 1MB No highlighting, lazy loading (first 10,000 lines)

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) 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 characters
Checkbox QCheckBox Value is true or false
Dropdown QComboBox Value starts with |, ≥2 pipes
File path QLineEdit + 📁 Value contains path pattern
Date QLineEdit + DATE Date keyword in key or date format
Text (default) AutoResizeTextEdit Everything else

QC script syntax coming soon

QC scripts are embedded in string fields of the qFORM file, compiled into a RestrictedPython sandbox and executed on rendering. The final expression result replaces the code block in the form.

Element Meaning
@( … )@ Marks a QC code block (also allowed multiple times in the same field)
<feldname> Placeholder – replaced by the value of a qFORM field before execution (dot notation possible for nested paths)
json_data The entire qFORM file as a dict – e.g. B. if an OSCAL file is opened directly in the Form tab
Allowed modules datetime, json, math, re, time, socket, urllib, optional requests (no direct file access)

Example 1 — Beginners: Pick-up date for a new passport

An authority qFORM records the day of the application and automatically calculates the earliest collection date (classic: application date + 30 days).

{
  "Reisepass-Antrag": {
    "antragsdatum": "DATUM 06.05.2026",
    "bearbeitungsdauer_tage": 30,
    "abholtermin": "QC: berechneter Termin: @(\nfrom datetime import datetime, timedelta\nantrag = datetime.strptime('<antragsdatum>', '%d.%m.%Y')\nabholung = antrag + timedelta(days=int('<bearbeitungsdauer_tage>'))\nf'{abholung.strftime(\"%A, %d.%m.%Y\")} (frühestens)'\n)@"
  }
}

This is how the QC field is rendered, for example: B. as:

berechneter Termin: Freitag, 05.06.2026 (frühestens)

What’s happening here?

  1. <antragsdatum> and <bearbeitungsdauer_tage> are replaced with the current field values before execution.
  2. datetime is available in the sandbox, the code calculates Antragstag + 30 Tage.
  3. The final expression (f"…") becomes the result - no more boilerplate is necessary.
  4. As soon as the clerk changes antragsdatum, mjEdit automatically updates the display.

Example 2 — Advanced: Count SSP implementation status

A pure OSCAL SSP file contains no QC field. However, there is the possibility of evaluating data via a temporary QC field and combining OSCAL files with qFORM in a meaningful way - either directly in the form tab or via an independent qFORM with a file picker (see below):

Variant A — Open 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 temporarily inserted in a free description field can evaluate the entire document - useful for sample statistics during a review:

@(
reqs = json_data.get('system-security-plan', {}).get('control-implementation', {}).get('implemented-requirements', [])
implementiert = 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"Implementiert: {implementiert} von {total} Anforderungen ({(implementiert/total*100 if total else 0):.1f} %)"
)@

Advantage: No additional tooling, can be used immediately. Disadvantage: The QC block must be removed before saving if the document is to remain schema valid.#### Variant B — Own “SSP statistics” qFORM with file picker

The clean version is an independent qFORM that records a file path to an SSP file and reads it in via urllib. The file path widget (📁 button) is natively supported by qFORM; the QC script accesses the value via <ssp_pfad>:

{
  "SSP-Statistik": {
    "ssp_pfad": "C:/oscal/ssp_acme.json",
    "report": "QC-Bericht: @(\nimport urllib.request, json\nfrom urllib.parse import urljoin\nfrom pathlib import PurePath\nuri = 'file:///' + str(PurePath('<ssp_pfad>')).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} von {len(reqs)} Controls implementiert'\n)@"
  }
}

Steps for the user:

  1. Open SSP statistics qFORM.
  2. Select the SSP file using the 📁 symbol.
  3. mjEdit replaces <ssp_pfad> with the selected path and executes the QC script.
  4. The result appears live in the form - every path change triggers a recalculation.

Advantage: Reusable, the SSP file itself remains unchanged, the QC script can deliver any complex evaluations (traffic lights per control family, missing components, POA&M references, etc.).

Markdown tab — Documentation and reports

Full-fledged Markdown editor with live preview in split view, GitHub Flavored Markdown, auto-formatting via mdformat and PDF/HTML export.

Advanced task list system

Element Syntax Example
Checkbox - [ ] / - [x] - [x] Server konfiguriert
Priority Keywords kritisch, wichtig, hoch
Status Keywords in arbeit, pausiert, vollständig
Assignment @Person @Mueller
Date 📅 YYYY-MM-DD 📅 2026-04-30
Tags #Tag #sicherheit #patch
OSCAL links &OSCAL|...|... Direct link to OSCAL Controls
POA&M References &POAM|...|... Direct link to measures

PDF Tab — Viewer, Annotator and Redaction Tool

PDF documents are displayed directly in mjEdit - with multi-tab support, annotations, 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 (■): Content is actually removed, not just covered over
  • Save PDF with all annotations as a new file

Browser tab — Built-in web browser

Full-fledged Chromium-based browser directly in 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 Dialogue with title, tags, abbreviations One click – no dialogue
Team sharing Shareable via Git Local only

Save web pages as an OSCAL back matter resource

Any web page displayed can be inserted into an open OSCAL document as a back matter resource by right-clicking - one click instead of copy-pasting between applications.

OSCAL Tabs (Plugin) — Specialized editors

The OSCAL tabs open automatically when mjEdit detects an OSCAL file. A tailor-made editor is available for each of the 8 OSCAL document types - see OSCAL integration.

AR object tree: Structure of an OSCAL assessment results document in mjEdit

File Tree View — Central Cockpit

  • Lazy loading for large directories (1,000+ files)
  • Colored file icons per extension (configurable)
  • Real-time filter, case-insensitive and recursive
  • Context menu with actions depending on file type
  • Backup indicator shows existing backups
  • Multi-selection for batch operations
  • “Open as text” bypasses automatic OSCAL routing

Backup and versioning concept

mjEdit offers a two-tier safety net:

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

A backup of the current file is automatically created before each restore.