What NSDdm is
nsddm reads Data Definition Modules (.NSD files) from a Software AG Natural fuser directory and converts them into structured JSON: file metadata, fields with format/length/descriptors, indexes and relationships between DDMs. It works in two mutually exclusive modes:
- Generation mode: scans the fuser and produces the JSON (
-fuserdirrequired). - Query mode: interrogates an already generated JSON without touching the fuser (
-inputrequired).
File and field names in the examples are fictional.
Use cases
1. Living DDM inventory
Regenerate the SYSTEM library JSON nightly and version it in git: any added, removed or changed field shows up in history.
nsddm -fuserdir /opt/softwareag/fuser -lib SYSTEM -outfile ddm-system.json
git add ddm-system.json && git commit -m "DDM inventory $(date +%F)"
2. Preparing an Adabas migration
Before migrating, list every file with its DBID, number and length to size the target and spot oddities (DB 0, extreme lengths).
nsddm -fuserdir /opt/softwareag/fuser -lib SYSTEM -outfile full.json
nsddm -input full.json -list number
3. Data-masking triage with NSAdaMask
Locate fields holding personal data (IDs, IBANs, emails, phones…) to anonymize them later with nsadamask. The category comes from the AI (-ia) or the name heuristic.
nsddm -input full.json -datamask
Typical output:
[228] IV2-DATOECON-COM NIF-TOMADOR A 14 conf=0.95 dni
[123] IV2-CARGAS-AUT NOMBRE-PERSONA A 50 conf=0.95 nombre
[108] IV2-DATORECI-T NUM-CUENTA-BCO N 16 conf=0.95 iban
4. Change impact analysis
The COD-PAIS field is about to change: find out in which files it appears and which master defines it.
nsddm -input full.json -search COD-PAIS
nsddm -input full.json -schema COD-PAIS -relations
5. Browsable data-model documentation
Combine -schema per file with the REL column (parent file) to map masters vs movements without opening Natural.
for f in 108 1100 228; do
nsddm -input full.json -schema $f
done
Command reference
Generation mode
| Parameter | Description | Required |
|---|---|---|
-fuserdir | Path to the Natural fuser directory (looks for .NSD in <fuserdir>/<lib>/SRC/ or <fuserdir>/SRC/) | Yes |
-lib | Library = subdirectory under fuserdir (e.g. SYSTEM). Default SYSTEM | No |
-outfile | Output JSON file (stdout if omitted) | No |
-files | Filter by number or range: 120, (100,200,300), (10-20,100-300), (10-20,50,100-300) | No |
-dbid | Filter by database. "", 000, 0000 and 0 all mean DB 0 | No |
-default-dbid | Assumed DBID for files whose NSD carries no DB | No |
-ia | AI enrichment: descriptions, sensitive data and relationships (requires -ai-key or AI_API_KEY) | No |
-ai-endpoint | OpenAI-compatible endpoint (default OpenRouter) | No |
-ai-key | API key (or AI_API_KEY variable) | With -ia |
-ai-model | Model (default deepseek/deepseek-chat) | No |
-ai-cache | AI response cache file (default ai_cache.json) | No |
Query mode (-input required, incompatible with generation flags)
| Parameter | Description |
|---|---|
-input | Previously generated JSON |
-schema | Full columnar schema of a DDM (full/partial name or file number) |
-relations | With -schema: per-field relationship detail (no effect without -schema) |
-datamask | Fields with sensitive data: file, field, format, length, confidence and category |
-search | Case-insensitive partial match on field name |
-fnr | DDMs containing that file number |
-list | Files ordered by DDM name (default) or by number (-list number) |
Common
| Parameter | Description |
|---|---|
-version | Version, build, date, host and platform |
-license | Path to license.key (default: automatic resolution) |
Incompatibilities
-inputwith generation flags (-fuserdir,-lib,-files,-dbid,-ia…): generation is ignored.-relationswithout-schema: no effect.-iawithout-ai-keyorAI_API_KEY: error.
Commented examples
# 1. Basic extraction to stdout
nsddm -fuserdir /opt/softwareag/fuser -lib SYSTEM
# 2. Only file 120 from CUSTOMER, saved
nsddm -fuserdir /opt/softwareag/fuser -lib CUSTOMER -files 120 -outfile c120.json
# 3. Combined range + DBID (quote so the shell leaves parentheses alone)
nsddm -fuserdir /opt/softwareag/fuser -lib ORDERS -dbid 50 \
-files '(10-20,100-300)' -outfile orders.json
# 4. AI-enriched (descriptions + sensitive + relationships)
export AI_API_KEY="your-key"
nsddm -fuserdir /opt/softwareag/fuser -lib SYSTEM -ia -outfile full.json
# A 2nd run over the same fuser is nearly instant (ai_cache.json)
# 5. Schema of a DDM and its relationships
nsddm -input full.json -schema IV2-POLIZA-T
nsddm -input full.json -schema 108 -relations
# 6. Where is NUM-CUENTA-BCO used?
nsddm -input full.json -search CUENTA
Output format (summary)
Each file contributes metadata (source_file, ddm_name, db, file_number, record_type, total_fields, total_length), fields (level, short name AA, field_name, format, length, descriptor, attribute —MU/PE/GR/SD—, remark, child fields, source_fields, comment, plus description, mask_category, mask_confidence when AI was used) and index.
The -schema REL column shows the parent (master) file of the many_to_one relationship —the DDM that defines the datum— or ←N when the field is a parent referenced by N files. Parent detection is deterministic (concept name + descriptor + master prefix FU-/MA-/TAB-); generic fields (dates, amounts…) produce no relationship.
Return codes and environment
| Code | Meaning |
|---|---|
0 | Success |
1 | Command-line parameter error |
2 | Filesystem error (directory not found, etc.) |
Progress goes to stderr; warnings don’t stop the run (unprocessable files are skipped). Variables: AI_API_KEY (AI key), LANG (message language), GOOS/GOARCH only for building from source (CGO_ENABLED=0 go build -o nsddm ., Go ≥ 1.24, no dependencies).
FAQ
Can I query without fuser access? Yes: generate the JSON once and distribute the file; -input never touches the fuser.
Do remarks with accents come out broken? No: ~30-char NSD chunks are joined and normalized ISO-8859-1 → UTF-8.
Does -datamask need AI? No: without -ia a deterministic name heuristic applies (NIF, NOMBRE, IBAN, TELEFONO, EMAIL, TARJETA, IP…); with -ia the model classifies with confidence.
Which DB do files without a DB field get? They normalize to 0; -dbid 0 includes them. -default-dbid can assume another value at generation time.