Privacy ingestion methods
Connect GitHub, publish from GitLab CI or GitHub Actions, or generate a privacy data map locally and send it with curl.
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 GitHub
Noru scans the GitHub repositories you select. Nothing to run in your pipeline.
Publish from GitLab CI or GitHub Actions
Check the map on every merge request or pull request and publish it after merge. Works with any Git host.
Generate locally and send with curl
A first import or a one-off update from your own machine, without CI.
| Connect GitHub | Publish from CI/CD | Locally with curl | |
|---|---|---|---|
| Works with | GitHub repositories | GitLab (gitlab.com or self-managed), GitHub, any CI | Any Git checkout |
| Where code is analysed | In Noru | In your pipeline | On your machine |
| Noru reads your repository | Yes, read-only, the repositories you select | No | No |
| You authenticate with | The privacy GitHub App | An API key with write:datamaps | An API key with write:datamaps |
| An update starts when | Someone clicks Scan now | A reviewed merge request or pull request merges | You send the request |
| You maintain | Repository selection and branch | Reviewed files, the check, the publish job | Reviewed 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:
| File | What it holds |
|---|---|
.noru/privacy-datamap.yml | Classifications, purposes, evidence, and named, dated review decisions. |
.noru/privacy-datamap.lock.json | The accepted baseline the check compares new code against. |
.fides/datamap.yml | The 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:
| Variable | Required | Value |
|---|---|---|
NORU_API_BASE_URL | Yes | https://api.noru.tech |
NORU_API_KEY | Yes | The API key, stored as a masked secret. |
NORU_SOURCE_SLUG | Yes | A stable, unique name for the source, such as the project path. Changing it creates a new source. |
NORU_SOURCE_NAME | No | Display name in Noru. Defaults to the slug. |
NORU_SOURCE_BRANCH | No | Branch the map came from, shown as provenance. |
NORU_SOURCE_COMMIT_SHA | No | Commit 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.
python3 "$TOOLKIT/plugins/privacy-datamap/scripts/check_privacy.py" \
--repo="$REPOSITORY"| Exit code | Meaning |
|---|---|
0 | Passed. |
1 | Blocked: the map needs review before this change merges. |
2 | The 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.
.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" --publishresource_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 code | Meaning |
|---|---|
0 | Published, or unchanged since the last push. |
1 | Validation or publication failed, or Noru returned taxonomy warnings. |
2 | The 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:
POST https://api.noru.tech/v1/privacy/datamaps
Authorization: Bearer <API key with write:datamaps>
Content-Type: application/jsonProp
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: trueand 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:
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
node "$scripts/collect.mjs" --repo="$REPOSITORY" --output=json
python3 "$scripts/reconcile.py" --repo="$REPOSITORY" --output=jsonOn 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
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=jsonThis 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:
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.
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)
PYKeep .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.
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:
{
"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.
Related
Last updated on