8 Loading and Importing Data

This chapter covers bringing data into TraxrGRC: what a new installation already holds, CSV imports per module, compliance framework packages, the bundle loader for a full migration over the API, a step-by-step plan for a first-time migration, and the demonstration data that must never be loaded on a production instance.

8.1 Choosing a method

MethodBest forRuns asWrites
CSV import (Import button on a list)Registers kept in spreadsheets; bulk updatesThe signed-in user, in the browserRisks, controls, assets, policies, third parties, incidents, projects, audit findings; new risk, compliance and policy exceptions
Account review CSV (Import CSV on a review program)The accounts to reviewThe signed-in userReview items
Compliance package import (Compliance → Packages → Import)A framework's requirement treeThe signed-in userOne package, its items and an analysis per item
Bundle loader (scripts/load-bundle.ts)A full, repeatable migration with requirement mappingsAn API tokenRisk scale, classifications, a package, controls, policies, risks, requirement mappings and statuses
REST APIAnything else, and ongoing integrationsAn API tokenMost records (see API Access and Integrations)

Every method goes through the same services and checks as the screens: the same validation, the same permission rules (including own-level access), and an Audit Log entry and History entry for every record.

8.2 What a new installation already contains

The migrate service adds reference data on every start, without changing or deleting anything that exists:

Nothing else: no users besides the first administrator, no groups, packages, labels, custom fields, incident classifications, business units or records.

8.3 CSV imports

Most module lists have an Import button beside New. On Risks, Policies and Compliance a menu offers the record type; audit findings are imported from the Audits page. The button appears only to people who may create or update those records (for exceptions, update on the parent records). The User Manual describes the dialog step by step (Getting Started, Importing from CSV); this section is the administrator's reference.

The Import controls dialog with the Download template and Download current records buttons.
Figure 8.1. The Import controls dialog with the Download template and Download current records buttons.

8.3.1 Record types and columns

ImportPagePermissionColumnsCustom fields
RisksRisksrisks:create or risks:updateid, title*, description, classification*, inherent_likelihood*, inherent_impact*, asset, business_unit, owner, owner_group, review_frequency_months, statusYes
ControlsControlscontrols:create or controls:updateid, name*, description, type, status, owner, owner_groupYes
AssetsAssetsassets:create or assets:updateid, name*, description, business_unit, owner, owner_groupNo
PoliciesPoliciespolicies:create or policies:updateid, title*, description, owner, owner_group, review_frequency_monthsYes
Third partiesThird PartiesthirdParties:create or thirdParties:updateid, name*, description, type, status, website, owner, owner_groupYes
IncidentsIncidentsincidents:create or incidents:updateid, title*, description, classification, opened_at, asset, third_party, owner, owner_group, status, resolutionYes
ProjectsProjectsprojects:create or projects:updateid, title*, description, status, deadline, owner, owner_groupYes
Audit findingsAuditsaudits:create or audits:updateid, audit_plan*, title*, description, status, deadline, control, owner, owner_groupNo
Risk exceptionsRisksrisks:updaterisk*, title*, description*, owner, approver, expires_at*No
Compliance exceptionsCompliancecompliance:updatepackage*, requirement*, title*, description*, owner, approver, expires_at*No
Policy exceptionsPoliciespolicies:updatepolicy*, title*, description*, owner, approver, expires_at*No

* Required when a row creates a record.

Allowed values:

ColumnValues
Risk statusopen, closed or archived
Risk inherent_likelihood, inherent_impactThe level's value or name from Risk Settings
Control typepolicy, process, technical, physical or other (process when blank)
Control statusplanned, active or retired (active when blank)
Third party typevendor, partner, customer or other (vendor when blank)
Third party statusactive, offboarding or terminated
Incident statusopen or closed; closing needs a resolution
Project statusplanned, in_progress or completed (planned when blank)
Audit finding statusopen, in_remediation or closed (updates only)
review_frequency_months1 to 120 (12 when blank on a new record)
Dates (opened_at, deadline, expires_at)YYYY-MM-DD

Values like statuses are matched without regard to case, spaces or underscores.

8.3.2 File rules

8.3.3 What an import does

  1. Check file runs every row through the record's own service inside a database transaction and then rolls it back. The result table shows what each row would do (Create, Update with the changed columns, Unchanged, or Error with every reason by line number). Nothing is saved.
  2. Import rows runs the same again and keeps the result only when every row succeeds. If any row fails at that moment, nothing is imported.
  3. Each record's History shows the change, marked as coming from "CSV import of" the file name, and one summary Audit Log entry (action import) records the file, the row count and the outcome counts.
  4. Notification rules ignore imported changes unless Notify on API changes and CSV imports is on (see Notifications and Email). Fixed reminders are unaffected: once imported, records are reminded like any other.

Download current records gives the records the importer can read, in the template's columns with ids filled in (at most 5,000). Edit it and import it back for bulk updates.

What CSV imports do not cover: policy content and approvals, risk treatments and reviews, control testing schedules, personal data breach details on incidents, the data map fields on assets, business units, audit plans, requirement mappings and anything in the privacy module. Use the app, the bundle loader or the REST API for those.

8.4 Compliance framework packages

A package is one framework (NIST SP 800-53, ISO/IEC 27001, the GDPR, a contract's security schedule) as a tree of headings and requirements. Each requirement gets an analysis, where its status is assessed.

8.4.1 Importing a package

Compliance → Packages → Import (needs compliance:create at all) reads a CSV or JSON file, previews the tree, and creates the package with Commit import. The file is refused as a whole when any code or name is empty, a code repeats, a parent_code names a code not in the file, or parents form a cycle.

The Import compliance package dialog, before a file is chosen.
Figure 8.2. The Import compliance package dialog, before a file is chosen.
FormatContent
CSVHeader with code and name, optionally description, parent_code (also parent) and kind. One row per item. The package name, standard and version are typed in the dialog.
JSON{ "name", "standard", "version", "description", "items": [ { "code", "name", "description", "parentCode", "kind" } ] }. Name and at least one item are required.

kind is requirement (blank means requirement; counts toward compliance), additional (listed, counted separately) or heading (structure only). Limits: up to 10,000 items, codes up to 100 characters, names up to 300.

Every package exports from its row on the Packages page as JSON in exactly this shape, so a package can be copied between instances.

The import dialog sends the whole package to the server in one request. Screen requests are limited to 1 MB by the web framework, so a very large framework file may be refused there. Load such a file through the REST API instead, which accepts the same JSON:

curl -X POST https://grc.example.com/api/v1/compliance/packages \
  -H "Authorization: Bearer $GRC_API_TOKEN" \
  -H "Content-Type: application/json" \
  --data-binary @package.json

The token needs compliance:create (see API Access and Integrations).

8.4.2 Framework content shipped with the source

TraxrGRC does not include licensed frameworks. Two public packages ship in the source and the container image, in the import JSON format, and can be imported on any instance:

FileContent
prisma/demo/nist-800-53-moderate.jsonNIST SP 800-53 Rev 5 Moderate baseline: 18 families as headings, 287 controls and enhancements with their control statements. Public domain in the United States. Provenance in prisma/demo/README.md.
prisma/demo/gdpr.jsonThe GDPR: 11 chapters as headings, 99 articles, 40 of them requirements and 59 additional. Descriptions are plain-language summaries written for the package, not the legal text.

To copy one out of the running container and import it:

docker compose cp web:/app/prisma/demo/nist-800-53-moderate.json .

Then import the file with Compliance → Packages → Import. These files only create a package; they do not load any demo records.

For other frameworks, use content you are licensed to use: a purchased toolkit, the standard body's machine-readable release, or an export from your previous tool, converted to the CSV or JSON shape above.

8.4.3 Mapping requirements

A requirement's analysis links it to the controls, policies and risks that address it, and to remediation projects. Mappings can be made:

CSV imports do not map requirements.

8.5 The bundle loader

scripts/load-bundle.ts loads a complete set of existing GRC data into an instance over the REST API: a risk scale, classifications, a compliance package, controls, policies, risks, and each requirement's mappings and status. It is built for go-live and for running again whenever the source changes. The loader is generic; a separate exporter, kept with the client's data and never in the TraxrGRC repository, turns the client's registers into a bundle file. The full format reference is docs/LOADING.md.

8.5.1 Before you run it

  1. A workstation with the source. The loader is not in the container image. Use a checkout of the TraxrGRC source at the same version as the instance, with Node.js 22 and its dependencies installed (npm ci). The workstation only needs HTTPS access to the instance.

  2. Outside approvals, if the bundle has them. Policies with effectiveDate and signatories need Settings → Policies → Allow recording outside approvals turned on.

  3. An API token created by an administrator (a token never exceeds its creator's rights), with these scopes:

    ModuleRightsWhy
    RisksRead, Create, UpdateRisks, treatments, reading the risk scale
    ControlsRead, Create, UpdateControls; reading them for mappings and treatments
    PoliciesRead, Create, Update, and Review when the bundle records outside approvalsPolicies and approvals
    ComplianceRead, Create, UpdatePackage, analyses and mappings
    SettingsCreate, UpdateClassifications and the risk scale

    Trimming this list breaks the load: scopes bound everything a request does, so mapping fails without Read on Controls, Policies and Risks. Give the token a short expiry and revoke it after go-live.

8.5.2 Running it

export GRC_API_TOKEN=aat_...
npm run load -- bundle.json --url https://grc.example.com            # dry run
npm run load -- bundle.json --url https://grc.example.com --apply    # write
OptionMeaning
--urlThe instance's address. Required.
--applyWrite. Without it the loader only reads the instance and reports what it would do.
--token-file <path>Read the token from a file, keeping it out of the shell history. Otherwise GRC_API_TOKEN is used.
--ca-file <path>Trust a private certificate authority, for an instance behind an internal CA (for example Caddy with tls internal).

The bundle is validated first; any problem stops the run before anything is read or written, listing up to 50 problems. The dry run then prints, per record kind, how many records it would create, update or leave unchanged, plus warnings. --apply does the same and writes. The command exits with status 1 if any write failed, so it can run in a pipeline.

Every write goes through the API, so it is validated, recorded in the Audit Log against the token, and sends no rule emails unless Notify on API changes and CSV imports is on.

8.5.3 Running it again

Each record in a bundle carries a ref, the source's permanent identifier, and its title or name starts with that ref followed by a space (R-01 Ransomware reaches...). A later run finds records by that prefix and updates them. Nothing is ever deleted, and owners are never set (assign them in the app). Highlights of the re-run rules:

The full table is in docs/LOADING.md.

8.5.4 Bundle format in brief

{
  "format": "grc-bundle",
  "version": 1,
  "source": "Exported from the 2026 risk register and SSP",
  "riskScale": { "likelihood": [], "impact": [], "cells": [], "bands": [] },
  "package": { "name": "...", "standard": "...", "version": "...", "items": [] },
  "classifications": [{ "name": "Availability" }],
  "controls": [{ "ref": "AC-2", "name": "AC-2 Account Management" }],
  "policies": [{ "ref": "POL-110", "title": "POL-110 Acceptable Use" }],
  "risks": [{ "ref": "R-01", "title": "R-01 Ransomware halts production" }],
  "requirements": [{ "code": "AC-2", "controls": ["AC-2"], "policies": ["POL-110"], "risks": ["R-01"] }]
}

Rules the validator enforces: refs unique per record type and up to 60 characters; titles and names start with their ref; free text up to 100,000 characters; residual likelihood and impact never above inherent; every risk's classification listed in classifications; with a riskScale, every risk's levels on that scale and a matrix cell for every pair; requirements only with a package, naming its item codes and refs in the same bundle; policies published from the bundle carry their content.

Each risk needs a residualBasis, which becomes an implemented mitigate treatment carrying the residual rating, and may carry a plannedTreatment.

8.6 First-time migration

Whether the data comes from spreadsheets or another GRC tool, load it in an order where everything a record refers to already exists. There is no direct importer for another vendor's database: export that tool's registers to CSV (or write an exporter to a bundle) and load them with the methods above.

  1. Freeze the target. Configure branding, SMTP and sign-in, but tell users the instance is not live yet. Decide whether rule emails stay suppressed during the load (the default).
  2. Settle the risk method. Set classifications, magnitudes, matrix and appetite bands in Risk Settings, or put them in the bundle's riskScale. Map the old tool's ratings to the new values in writing.
  3. People. Create users and groups, or let SSO, LDAP or SCIM create them (see Authentication and Single Sign-On). Owners in every import must be active accounts, and owner groups must exist.
  4. Supporting lists. Create business units (Risk Management → Business Units, or /api/v1/business-units), incident classifications, labels, custom fields and audit plans. None of these has a CSV import.
  5. Catalogs. Import controls, assets and third parties.
  6. Frameworks. Import compliance packages.
  7. Policies. Import titles, owners and review frequencies by CSV, then add content and approvals in the app, or load them by bundle with content and outside approvals.
  8. Registers. Import risks (linking assets and business units), incidents (linking assets and third parties), projects, and audit findings (naming their plan and control).
  9. Exceptions. Import risk, compliance and policy exceptions once their parent records exist. They arrive as requested; decide them in the app.
  10. Mappings and treatments. Map requirements and add risk treatments in the app, by bundle, or over the API.
  11. Check. Run the Report Center reports, compare counts with the source, and open a sample of records. Review the Audit Log entries for the load.
  12. Before go-live, look ahead at reminders: the first daily sweep after the load emails owners about every review, control test and expiring exception that is due or overdue in the loaded data. Correct wrong dates first, and tell owners to expect the emails.
  13. Revoke migration tokens and switch on any notification rules.

Keep the source files and the loader output with the migration records; the Audit Log entries carry the token and file names that tie each change back to them.

8.7 Demo data

The source includes a sales demonstration: a fictional company, Quillmont Systems, with records in every module, NIST SP 800-53 Moderate and the GDPR as packages, notification rules, scheduled reports and eight sign-in personas. It is loaded by prisma/demo-seed.ts (npm run db:demo) and described in docs/DEMO.md.

Never load the demo on an instance that will hold real data. It creates fictional people with working sign-in accounts (one of them an administrator), dozens of fictional records across every module, notification rules and scheduled reports that email those people, and sets branding and settings (product name "TraxrGRC", outside approvals on). Its records are created through the normal services, so they are written into the hash-chained Audit Log, which cannot be edited or pruned for a year. There is no way to remove the demo afterward; the only clean path is a new, empty database.

Two safeguards exist:

To show the product, run the demo on a separate instance with its own database and its own address (see docs/DEMO.md), and keep its accounts file private.