Privacy

Privacy ingestion methods

Connect GitHub, publish from GitLab CI or GitHub Actions, or generate a privacy data map locally and send it with curl.

RolesEditorAdminRoute/privacy/data-mapShown toPrivacy organizations

What it is

The data map is built from evidence about your code: which systems exist, the personal data they process, and why. There are three ways to get that evidence into Noru. All three end in the same place, a source under PrivacyData mapSources with its own version history, ready for review in Records of processing.

Choose an approach

Connect GitHubPublish from CI/CDLocally with curl
Works withGitHub repositoriesGitLab (gitlab.com or self-managed), GitHub, any CIAny Git checkout
Where code is analysedIn NoruIn your pipelineOn your machine
Noru reads your repositoryYes, read-only, the repositories you selectNoNo
You authenticate withThe privacy GitHub AppAn API key with write:datamapsAn API key with write:datamaps
An update starts whenSomeone clicks Scan nowA reviewed merge request or pull request mergesYou send the request
You maintainRepository selection and branchReviewed files, the check, the publish jobReviewed files and the request

One publisher per source

Each source is identified by its slug, and every push replaces that source's map. If two jobs publish different maps under the same slug, each run undoes the other. Pick one method per source and keep it.

Connect GitHub

Use this when you want Noru to read the code for you and you would rather not run anything in your own pipeline.

Open PrivacyData map, click Sources, then Connect, and choose Connect GitHub. On an empty map the button is Connect data source.

Click Connect a GitHub account, then install or authorize the privacy GitHub App in GitHub and pick the repositories it may read. Your GitHub organization may require an owner to approve the installation.

Back in Noru, select the repositories and click Connect selected.

Expand each repository to check its branch. Scans use the saved branch; change it and click Save branch before scanning.

Click Scan now. When the status reads Scanned, the systems and datasets appear in the data map. Scan again whenever you want to refresh them.

Connecting repositories and starting scans need the editor or admin role. If Connect GitHub is not offered, repository scanning is not enabled for your organization yet: use one of the API methods below, or ask Noru to enable it.

Two different GitHub Apps

The privacy GitHub App reads repository contents so it can find personal data in schemas and code. It is separate from the GitHub compliance connector, which collects repository and security evidence and never reads source code. Connecting one does not connect the other.

On GitLab?

Repository scanning connects to GitHub today. For GitLab projects, publish from GitLab CI: you get the same data map, plus a check that blocks merge requests when the map falls out of date. The GitLab compliance connector collects project and security evidence only and does not feed the data map.

Publish from CI/CD

Use this for GitLab projects (gitlab.com or self-managed), for GitHub repositories when you want privacy changes reviewed in the same pull request as the code, or for any other Git host. Noru receives only the manifest your pipeline sends. It never connects to your repository, and the manifest describes schemas and processing, not customer records.

The examples use the privacy-datamap plugin from Noru GRC Engineering v0.11.0 or later. Any tool that produces a valid Fideslang manifest works with the REST API instead. Pick your CI below; every tab on this page follows your choice.

You need a runner that can reach https://api.noru.tech over HTTPS. Shared gitlab.com runners can; on self-managed GitLab, allow that egress for the runners that run the publication job. Nothing has to reach your GitLab instance from outside.

Generate and review the map

Run the plugin locally, ideally with an agent to explain how the code uses each field. Someone on your team reviews every classification and purpose and records the decision. The plugin writes three files you commit next to the code:

privacy-datamap.yml
privacy-datamap.lock.json
datamap.yml
FileWhat it holds
.noru/privacy-datamap.ymlClassifications, purposes, evidence, and named, dated review decisions.
.noru/privacy-datamap.lock.jsonThe accepted baseline the check compares new code against.
.fides/datamap.ymlThe generated Fideslang export. This is what Noru receives.

Fields you confirm as non-personal stay in the reviewed manifest and the baseline but are left out of the export. The local walkthrough shows the commands.

Store the API key as a CI secret

In the Noru organization that should receive the map, an admin creates an API key with the write:datamaps scope. The key decides the organization; the slug decides which source inside it.

write:datamaps authorizes every privacy write through the API, including records of processing and assessments, not only data map pushes. Expose the key to the publication job alone, never to merge request or pull request jobs.

In the project (or its group, to share the key across projects), open SettingsCI/CDVariables and add NORU_API_KEY. Set its visibility to Masked and tick Protect variable, so only pipelines on protected branches receive it. Make sure your default branch is protected.

The publisher reads these variables:

VariableRequiredValue
NORU_API_BASE_URLYeshttps://api.noru.tech
NORU_API_KEYYesThe API key, stored as a masked secret.
NORU_SOURCE_SLUGYesA stable, unique name for the source, such as the project path. Changing it creates a new source.
NORU_SOURCE_NAMENoDisplay name in Noru. Defaults to the slug.
NORU_SOURCE_BRANCHNoBranch the map came from, shown as provenance.
NORU_SOURCE_COMMIT_SHANoCommit the map came from, shown as provenance.

The publisher never reads CI-provider variables on its own, so map your provider's branch and commit to the last two, as the examples below do.

Require the privacy check before merge

The check runs offline and needs no credentials. It fails when the code and the reviewed map disagree: unknown taxonomy values, unresolved or expired reviews, schema drift, changed processing evidence, or an export that no longer matches the accepted map.

Privacy check
python3 "$TOOLKIT/plugins/privacy-datamap/scripts/check_privacy.py" \
  --repo="$REPOSITORY"
Exit codeMeaning
0Passed.
1Blocked: the map needs review before this change merges.
2The tool could not run, for example a wrong path.

The privacy-check job in the next step runs in every pipeline. Open SettingsMerge requests and, under Merge checks, enable Pipelines must succeed so a blocked check stops the merge.

The check only watches processing evidence that is registered in the baseline; anything outside that is not monitored.

Publish after merge

On the default branch, run the check again and publish only if it passes. Keep the API key in this job alone, run one publication per source at a time, and stop older runs from publishing after newer ones.

.gitlab-ci.yml
.privacy-toolkit:
  # Use the Python minor version the baseline was sealed with.
  image: python:3.12
  variables:
    TOOLKIT: /tmp/noru-grc-engineering
  before_script:
    - apt-get update -qq && apt-get install -y -qq nodejs > /dev/null
    - >-
      git clone --depth 1 --branch v0.11.0
      https://github.com/noru-tech/noru-grc-engineering.git "$TOOLKIT"

privacy-check:
  extends: .privacy-toolkit
  stage: test
  script:
    - >-
      python3 "$TOOLKIT/plugins/privacy-datamap/scripts/check_privacy.py"
      --repo="$CI_PROJECT_DIR"

privacy-publish:
  extends: .privacy-toolkit
  stage: deploy
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
  resource_group: privacy-datamap # one publication at a time
  variables:
    NORU_API_BASE_URL: https://api.noru.tech
    NORU_SOURCE_SLUG: $CI_PROJECT_PATH
    NORU_SOURCE_BRANCH: $CI_COMMIT_BRANCH
    NORU_SOURCE_COMMIT_SHA: $CI_COMMIT_SHA
  script:
    # NORU_API_KEY comes from the masked, protected CI/CD variable.
    - >-
      python3 "$TOOLKIT/plugins/privacy-datamap/scripts/check_privacy.py"
      --repo="$CI_PROJECT_DIR"
    - >-
      python3 "$TOOLKIT/plugins/privacy-datamap/scripts/publish_datamap.py"
      "$CI_PROJECT_DIR/.fides/datamap.yml" --publish

resource_group runs one publication at a time. To also stop an older pipeline from publishing after a newer one, set the resource group's process mode to newest first. The toolkit is cloned to /tmp, outside the project checkout, so it is never scanned with your code.

Exit codeMeaning
0Published, or unchanged since the last push.
1Validation or publication failed, or Noru returned taxonomy warnings.
2The command was called incorrectly.

A failed publish may still have written

Taxonomy warnings make the publisher exit with 1 after Noru has already accepted the map, and a timed-out request may have completed. Check the source in Noru before you retry. The publisher never retries an uncertain write on its own.

Agents help, people decide

An agent can draft the map and explain how the code uses a field, but the check and the publisher run without one, and neither makes a privacy decision. CI only verifies decisions someone already recorded.

Use the REST API directly

Your own publisher can call the endpoint the plugin uses:

Request
POST https://api.noru.tech/v1/privacy/datamaps
Authorization: Bearer <API key with write:datamaps>
Content-Type: application/json

Prop

Type

Each push replaces the source's snapshot; it never merges a partial update.

  • Send the complete map. Anything the manifest no longer names is archived (not deleted) in Noru.
  • Identical content is a no-op. The response has unchanged: true and no new version is created. Branch and commit are not part of the comparison, so pushing the same map from a new commit only refreshes its provenance.
  • Read the warnings. A successful response can still list taxonomy keys that did not resolve. Fix them in the map and push again.

The REST API guide and the public API specification have the full schema.

Generate locally and send with curl

Use this for a first import, a manual update, or a team without CI automation. You need a local Git checkout of the application, the GRC Engineering toolkit, Git, Node.js 18+, Python 3.9+, and an API key with write:datamaps. No third-party Python packages are needed.

Set two paths first. Keep the toolkit outside the application checkout so it is not scanned along with your code:

Shell
export TOOLKIT=~/src/noru-grc-engineering   # pinned to a reviewed release
export REPOSITORY=~/src/customer-service      # the application to map
scripts="$TOOLKIT/plugins/privacy-datamap/scripts"

Collect and reconcile

Collect
node "$scripts/collect.mjs" --repo="$REPOSITORY" --output=json
python3 "$scripts/reconcile.py" --repo="$REPOSITORY" --output=json

On a first run this creates .noru/privacy-datamap.yml with every field flagged for review. On later runs, proposed changes land in .noru/.cache/ and accepted decisions are never overwritten.

Review the proposed map

Follow the privacy-datamap review workflow: look at how the code uses each field, settle its classification and purpose, and record a named, dated review with an expiry. Resolve any datastore-boundary, discovery, or evidence questions before you accept.

Do not clear review flags just to make validation pass. An unfamiliar field needs a look in context: a random run ID and a customer ID are not the same kind of data.

Validate, seal, and export

Validate, seal, export
python3 "$scripts/validate_manifest.py" \
  "$REPOSITORY/.noru/privacy-datamap.yml" \
  --emit-parsed="$REPOSITORY/.noru/.cache/privacy-datamap.parsed.json" && \
python3 "$scripts/reconcile.py" --repo="$REPOSITORY" --seal --output=json && \
node "$scripts/collect.mjs" --repo="$REPOSITORY" --output=json

This seals the accepted baseline and writes .fides/datamap.yml. Commit the three files with the code changes they describe, then run the privacy check once more:

Check
python3 "$scripts/check_privacy.py" --repo="$REPOSITORY"

Build the request body

The API takes JSON, so the YAML export is parsed with the toolkit's own reader and wrapped with the slug and provenance. The result goes to the ignored cache folder and never contains the API key.

Build the request body
export NORU_SOURCE_SLUG=customer-service   # stable, unique per source
python3 - <<'PY'
import json, os, subprocess, sys
from pathlib import Path

repo = Path(os.environ["REPOSITORY"]).expanduser().resolve()
sys.path.insert(0, str(Path(os.environ["TOOLKIT"]).expanduser() / "plugins/privacy-datamap/scripts"))
from publish_datamap import read_manifest

def git(*args):
    return subprocess.check_output(["git", "-C", str(repo), *args], text=True).strip()

payload = {
    "slug": os.environ["NORU_SOURCE_SLUG"],
    "manifest": read_manifest(repo / ".fides/datamap.yml"),
    "commitSha": git("rev-parse", "HEAD"),
}
if branch := git("branch", "--show-current"):
    payload["branch"] = branch

out = repo / ".noru/.cache/datamap-request.json"
out.parent.mkdir(parents=True, exist_ok=True)
out.write_text(json.dumps(payload, allow_nan=False), encoding="utf-8")
print(out)
PY

Keep .noru/.cache/ out of version control.

Send it

Load the key from your password manager or a local secret store rather than typing it into a script you might commit.

Send with curl
curl --fail-with-body --silent --show-error --max-time 60 \
  --request POST "https://api.noru.tech/v1/privacy/datamaps" \
  --header "Authorization: Bearer $NORU_API_KEY" \
  --header "Content-Type: application/json" \
  --header "Accept: application/json" \
  --data-binary "@$REPOSITORY/.noru/.cache/datamap-request.json"

A successful response looks like this:

Response
{
  "data": {
    "dataSourceId": "NORU-PSRC-1",
    "unchanged": false,
    "counts": { "systems": 2, "datasets": 3, "activities": 4 },
    "warnings": []
  }
}

The response also carries the new version's id and a change summary. curl fails on HTTP errors but not on warnings, so read them yourself. If the request times out, check the source in Noru before retrying: the write may already have completed.

Review the result in Noru

Open PrivacyData mapSources to see the source, its branch and commit, its version history, and what changed. Then review the resulting activities in Records of processing.

What Noru does not do

A data map records what the code processes and why. It does not establish legal basis, retention, or recipients, and importing or scanning one does not approve anything. Those decisions belong to the processing records, where a person confirms them.

Last updated on