The KAGAMI mark КАГАМИ
kagami.bg/academy · lesson · machine-readable viewUPDATED 2026-10-03
IDENTITY
module
GX10-04-162 · Register and drafts for GDPR Art. 28 processor contracts
series
GX10 (local AI server class: NVIDIA GB10, e.g. ASUS Ascent GX10 / DGX Spark)
level
Advanced
duration
2-3 h
prerequisites
A GB10-class machine with Ollama, Postgres and n8n (see 04-01), Python 3.10+; access to a lawyer or data protection officer for review
trust_label
UPDATED 2026-10-03 (full text of Article 28 and Article 83(4) of Regulation (EU) 2016/679, Article 21(2) of Directive (EU) 2022/2555 and the titles of Commission Implementing Decisions (EU) 2021/914 and 2021/915 read on EUR-Lex on 2026-10-03; Ollama model pages and PyPI versions) · NOT TESTED on a GB10 machine · no VERIFIED label · NOT LEGAL ADVICE · all providers in the lesson are invented
versions
qwen2.5:14b = 9.0 GB, Apache-2.0 (ollama.com) · jinja2 3.1.6 · httpx 0.28.1 (PyPI, 2026-10-03)
language
human view: bg · english edition: /en/academy/gx10/ (same file name)
previous / next
04-161_Insider_Risk_Operator_Center.html / 04-163_Smart_Venue_IoT.html
PURPOSE

Teach a controller how to keep a register of processor contracts under GDPR Art. 28 in Postgres (append-only versions, a dashboard view, expiry states), how to produce a draft contract from a template where the mandatory processor obligations of Art. 28(3) are inserted verbatim from EUR-Lex and a local model fills only four descriptive fields from a human-written service description, and how to send daily reminders from n8n. A lawyer or DPO must review every draft; the lesson is not legal advice.

KEY CONCEPTS
COMMANDS / PATHS
CHECKLIST
NEXT MODULE

04-163 · Smart building sensors (04-163_Smart_Venue_IoT.html) · related: 04-142 GDPR assistant, 04-185 NIS2 suppliers · series index: kagami.bg/en/academy/gx10/ · offer: Quick experiment (kagami.bg/stalbata/)

SOURCES
TAGS
gx10nvidia-gb10gdprarticle-28postgresollaman8nnot-legal-advice
UPDATED · 03.10.2026

GDPR Art. 28 Contracts with a Local AI

When a company uses an outside service that processes personal data (cloud storage, a payroll system, email), Article 28 of the GDPR requires a contract. Here we build a register of those contracts in Postgres, a draft from a template that a local model helps to fill, and reminders for expiry. The lesson is not legal advice and does not replace a lawyer. The providers in the examples are invented.

⏱ 2–3 h Advanced GX10 GDPR Art. 28 · Postgres · Ollama · n8n
Postgres (the register)🔒 local Ollama · qwen2.5:14b (fills the descriptive fields)🔒 local Jinja2 (the template)🔒 local n8n (the reminders)🔒 local
🔄
UPDATED · 03.10.2026 — what changed
We made the example generic. The lesson is no longer about a particular company or sector; instead of real providers and dates we use invented ones. We changed the approach: the old code had the model "invent" the clauses. Now the mandatory processor obligations of Article 28(3) are inserted verbatim from EUR-Lex, and the model fills only four descriptive fields, each marked "DRAFT — check". The legal parts stay with the lawyer. The text of the law: we give the full text of Article 28 from EUR-Lex (Bulgarian and English versions), as well as Article 83(4)(a) and Article 21(2)(d) of Directive (EU) 2022/2555. We corrected: the fine is "up to 10 000 000 EUR or up to 2 % of turnover — whichever is higher" (the old text left out "whichever is higher"); the SQL schema was reworked (a view with the latest version, states are computed); the "YAML workflow" was fictitious — n8n stores workflows as JSON, so we describe the nodes. We removed: the claim that the supervisory authority must be notified of certain transfers (we did not find it in the text of Article 28), specific provider names and a table of "real" dates, and the cross-references to another lesson. We added: the Commission's standard contractual clauses (Implementing Decision (EU) 2021/915 for controller–processor contracts and (EU) 2021/914 for transfers to third countries).
⚠️
What we have not run ourselves
The code and SQL have not been run on a GB10-class machine — there is no "TESTED" label and no "VERIFIED". The text of the law was checked against EUR-Lex on 03.10.2026, but this is not legal advice: every draft, every contract and every decision about transfers outside the EU/EEA is reviewed by a lawyer or by the data protection officer.

01What you'll learn

02Before you start

⚖️
Not legal advice
The lesson explains how to organise your work. It does not say which of your providers are "processors", what your contract must contain, or whether another rule applies. That is a question for a lawyer.

03Steps

  1. What Article 28 says

    Why the law first: everything that follows is organisation around this text. In it "another processor" (paragraphs 2 and 4) is what practice calls a sub-processor. Here is the full text of Article 28 of Regulation (EU) 2016/679 — the official English version from EUR-Lex, read on 03.10.2026:

    Article 28 · Processor

    1. Where processing is to be carried out on behalf of a controller, the controller shall use only processors providing sufficient guarantees to implement appropriate technical and organisational measures in such a manner that processing will meet the requirements of this Regulation and ensure the protection of the rights of the data subject.

    2. The processor shall not engage another processor without prior specific or general written authorisation of the controller. In the case of general written authorisation, the processor shall inform the controller of any intended changes concerning the addition or replacement of other processors, thereby giving the controller the opportunity to object to such changes.

    3. Processing by a processor shall be governed by a contract or other legal act under Union or Member State law, that is binding on the processor with regard to the controller and that sets out the subject-matter and duration of the processing, the nature and purpose of the processing, the type of personal data and categories of data subjects and the obligations and rights of the controller. That contract or other legal act shall stipulate, in particular, that the processor:

    (a) processes the personal data only on documented instructions from the controller, including with regard to transfers of personal data to a third country or an international organisation, unless required to do so by Union or Member State law to which the processor is subject; in such a case, the processor shall inform the controller of that legal requirement before processing, unless that law prohibits such information on important grounds of public interest;
    (b) ensures that persons authorised to process the personal data have committed themselves to confidentiality or are under an appropriate statutory obligation of confidentiality;
    (c) takes all measures required pursuant to Article 32;
    (d) respects the conditions referred to in paragraphs 2 and 4 for engaging another processor;
    (e) taking into account the nature of the processing, assists the controller by appropriate technical and organisational measures, insofar as this is possible, for the fulfilment of the controller's obligation to respond to requests for exercising the data subject's rights laid down in Chapter III;
    (f) assists the controller in ensuring compliance with the obligations pursuant to Articles 32 to 36 taking into account the nature of processing and the information available to the processor;
    (g) at the choice of the controller, deletes or returns all the personal data to the controller after the end of the provision of services relating to processing, and deletes existing copies unless Union or Member State law requires storage of the personal data;
    (h) makes available to the controller all information necessary to demonstrate compliance with the obligations laid down in this Article and allow for and contribute to audits, including inspections, conducted by the controller or another auditor mandated by the controller.

    With regard to point (h) of the first subparagraph, the processor shall immediately inform the controller if, in its opinion, an instruction infringes this Regulation or other Union or Member State data protection provisions.

    4. Where a processor engages another processor for carrying out specific processing activities on behalf of the controller, the same data protection obligations as set out in the contract or other legal act between the controller and the processor as referred to in paragraph 3 shall be imposed on that other processor by way of a contract or other legal act under Union or Member State law, in particular providing sufficient guarantees to implement appropriate technical and organisational measures in such a manner that the processing will meet the requirements of this Regulation. Where that other processor fails to fulfil its data protection obligations, the initial processor shall remain fully liable to the controller for the performance of that other processor's obligations.

    5. Adherence of a processor to an approved code of conduct as referred to in Article 40 or an approved certification mechanism as referred to in Article 42 may be used as an element by which to demonstrate sufficient guarantees as referred to in paragraphs 1 and 4 of this Article.

    6. Without prejudice to an individual contract between the controller and the processor, the contract or the other legal act referred to in paragraphs 3 and 4 of this Article may be based, in whole or in part, on standard contractual clauses referred to in paragraphs 7 and 8 of this Article, including when they are part of a certification granted to the controller or processor pursuant to Articles 42 and 43.

    7. The Commission may lay down standard contractual clauses for the matters referred to in paragraph 3 and 4 of this Article and in accordance with the examination procedure referred to in Article 93(2).

    8. A supervisory authority may adopt standard contractual clauses for the matters referred to in paragraph 3 and 4 of this Article and in accordance with the consistency mechanism referred to in Article 63.

    9. The contract or the other legal act referred to in paragraphs 3 and 4 shall be in writing, including in electronic form.

    10. Without prejudice to Articles 82, 83 and 84, if a processor infringes this Regulation by determining the purposes and means of processing, the processor shall be considered to be a controller in respect of that processing.

    💡
    What to take from the text for the register
    The contract is written (paragraph 9). It describes subject-matter, duration, nature, purpose, type of data and categories of data subjects (paragraph 3). For every other processor there must be authorisation and the same obligations down the chain (paragraphs 2 and 4). So the register keeps, for each provider: the service, the types of data, the versions of the contract and its expiry.
  2. Two more quotations: fines and supply chains

    Fines. Article 83(4)(a) of the same regulation, official English text from EUR-Lex:

    Infringements of the following provisions shall, in accordance with paragraph 2, be subject to administrative fines up to 10 000 000 EUR, or in the case of an undertaking, up to 2 % of the total worldwide annual turnover of the preceding financial year, whichever is higher:
    (a) the obligations of the controller and the processor pursuant to Articles 8, 11, 25 to 39 and 42 and 43;

    Article 28 falls within "25 to 39". The actual amount in each case is decided by the supervisory authority.

    Supply chains (NIS2). Directive (EU) 2022/2555 is a different act and applies only to entities within its scope — whether you are one is a question for a lawyer. Article 21(2)(d) (official English text from EUR-Lex) says that the risk-management measures shall include at least:

    (d) supply chain security, including security-related aspects concerning the relationships between each entity and its direct suppliers or service providers;

    The register we build is useful for both — but it is an organisational tool, not evidence of compliance by itself.

  3. Ready-made standard clauses

    Article 28(6)–(8) allows the contract to be based on standard contractual clauses. The titles of the two Commission acts, as they are on EUR-Lex:

    • Commission Implementing Decision (EU) 2021/915 of 4 June 2021 — standard contractual clauses between controllers and processors under Article 28(7) of Regulation (EU) 2016/679 and Article 29(7) of Regulation (EU) 2018/1725.
    • Commission Implementing Decision (EU) 2021/914 of 4 June 2021 — standard contractual clauses for the transfer of personal data to third countries pursuant to Regulation (EU) 2016/679.

    Read them before you write your own template: your lawyer may prefer them. Whether and when a separate mechanism is needed for a transfer outside the EU/EEA is a question for them — we give no "rule" here.

  4. A register in Postgres

    Why versions: a contract changes and is renewed; the old versions must remain as evidence. A new version is a new row, and the old one gets the status superseded. Why the states are computed: "expired" is not a fact you record, it follows from the date. Everything below is an invented example.

    sql · schema.sql · not run
    CREATE TABLE processors (
      id             serial PRIMARY KEY,
      name           text NOT NULL UNIQUE,
      service        text NOT NULL,
      data_types     text,
      outside_eea    boolean NOT NULL DEFAULT false,
      transfer_basis text,                 -- filled in together with the lawyer
      created_at     timestamptz NOT NULL DEFAULT now()
    );
    
    CREATE TABLE dpa_versions (
      id             serial PRIMARY KEY,
      processor_id   integer NOT NULL REFERENCES processors(id),
      version_number integer NOT NULL,
      content_md     text NOT NULL,
      status         text NOT NULL DEFAULT 'draft'
                     CHECK (status IN ('draft','pending_signature','active','superseded')),
      signed_date    date,
      expiry_date    date,
      reviewed_by    text,                 -- who did the legal review
      created_at     timestamptz NOT NULL DEFAULT now(),
      UNIQUE (processor_id, version_number)
    );
    
    -- one row per provider: the latest version and a computed state
    CREATE VIEW dpa_dashboard AS
    SELECT DISTINCT ON (p.id)
      p.name AS processor,
      p.outside_eea,
      v.version_number,
      v.status,
      v.expiry_date,
      (v.expiry_date - CURRENT_DATE) AS days_left,
      CASE
        WHEN v.id IS NULL                          THEN 'MISSING'
        WHEN v.status <> 'active'                  THEN upper(v.status)
        WHEN v.expiry_date IS NULL                 THEN 'NO_EXPIRY'
        WHEN v.expiry_date < CURRENT_DATE          THEN 'EXPIRED'
        WHEN v.expiry_date - CURRENT_DATE <= 60    THEN 'EXPIRING'
        ELSE 'VALID'
      END AS state
    FROM processors p
    LEFT JOIN dpa_versions v ON v.processor_id = p.id
    ORDER BY p.id, v.version_number DESC;
    
    -- versions are never deleted: take the right away from the application
    -- REVOKE DELETE ON dpa_versions FROM <application-role>;

    The number 60 is your own choice for "expires soon", not a requirement of the law. This is what the view looks like for invented providers as of 03.10.2026:

    ProviderServiceExpiresDaysStateOutside EEA
    Provider ACloud storage2027-03-01149VALIDno
    Provider BPayroll system2026-11-2048EXPIRINGno
    Provider CStaff system2026-08-15−49EXPIREDno
    Provider DEmail2027-01-1099VALIDno
    Provider ESMS notifications——MISSINGyes

    Row "E" is the most important: a service that touches data with no contract in the register. Those are the cases you look for in the inventory.

  5. A draft from a template — the model fills only the descriptive fields

    Why this way: the mandatory obligations of the processor were written by the legislator — we do not "translate" them through a model. Copy the text of paragraph 3 verbatim (from "That contract or other legal act shall stipulate…" to the end of point (h) and the last subparagraph) from EUR-Lex into a file art28_3.txt, in the language of the contract. The model gets only the service description written by a person and returns four short fields; each is marked, and numbers that are not in the description are flagged.

    python · dpa_draft.py · not run
    # dpa_draft.py · a draft processing agreement (invented example)
    import json
    import pathlib
    import re
    
    import httpx
    from jinja2 import Template
    
    OLLAMA = "http://localhost:11434"
    MODEL = "qwen2.5:14b"
    
    # The text of Art. 28(3) — copied verbatim from EUR-Lex
    ART28_3 = pathlib.Path("art28_3.txt").read_text(encoding="utf-8")
    
    TEMPLATE = Template("""# DATA PROCESSING AGREEMENT — DRAFT
    Basis: Article 28 of Regulation (EU) 2016/679.
    
    **Controller:** <controller>
    **Processor:** {{ processor }}
    
    ## 1. Subject-matter and duration
    {{ subject_and_duration }}
    
    ## 2. Nature and purpose of the processing
    {{ nature_and_purpose }}
    
    ## 3. Type of personal data and categories of data subjects
    {{ data_types }}
    {{ data_subjects }}
    
    ## 4. Obligations and rights of the controller
    [to be completed by the lawyer]
    
    ## 5. Obligations of the processor (Art. 28(3), verbatim)
    {{ art28_3 }}
    
    ## 6. Other processors
    [to be completed by the lawyer: Art. 28(2) and (4)]
    
    ## 7. Transfers outside the EU/EEA
    [to be completed by the lawyer: see Chapter V of the regulation and the standard clauses]
    
    Date: <date>    Signatures: <controller> / {{ processor }}
    """)
    
    SCHEMA = {
        "type": "object",
        "properties": {
            "subject_and_duration": {"type": "string"},
            "nature_and_purpose": {"type": "string"},
            "data_types": {"type": "string"},
            "data_subjects": {"type": "string"},
        },
        "required": ["subject_and_duration", "nature_and_purpose", "data_types", "data_subjects"],
    }
    
    
    def fill(service: str) -> dict:
        prompt = (
            "Service description (written by a person):\n" + service
            + "\n\nFill four fields in English for a draft data processing agreement. "
              "Use ONLY what is described. If something is not described, write "
              "'[missing from the description]'. Do not add legal statements, periods or amounts."
        )
        r = httpx.post(
            f"{OLLAMA}/api/chat",
            json={
                "model": MODEL,
                "stream": False,
                "format": SCHEMA,
                "messages": [{"role": "user", "content": prompt}],
                "options": {"temperature": 0},
            },
            timeout=300,
        )
        r.raise_for_status()
        return json.loads(r.json()["message"]["content"])
    
    
    def check(fields: dict, service: str) -> list[str]:
        problems = []
        allowed = set(re.findall(r"\d+", service))
        for key, val in fields.items():
            if not val.strip():
                problems.append(f"{key}: empty")
            extra = set(re.findall(r"\d+", val)) - allowed
            if extra:
                problems.append(f"{key}: numbers not in the description: {sorted(extra)}")
        return problems
    
    
    def draft(processor: str, service: str) -> tuple[str, list[str]]:
        fields = fill(service)
        problems = check(fields, service)
        marked = {k: "[DRAFT — check] " + v for k, v in fields.items()}
        return TEMPLATE.render(processor=processor, art28_3=ART28_3, **marked), problems
    
    
    if __name__ == "__main__":
        text, problems = draft(
            "Provider A",
            "Cloud storage for the organisation's documents. Staff upload files that "
            "may contain names and email addresses. A one-year contract.",
        )
        print(text)
        print("Problems:", problems or "none")

    Run it first with three invented providers of different kinds and read the result line by line. If a field is wrong, you fix the description, not the text of the law.

  6. Reminders in n8n

    n8n workflows are stored as JSON and built from nodes; for the details of each node see its page (see Sources). One workflow is enough:

    NodeSetting
    Schedule Triggerevery morning
    Postgresquery: SELECT processor, state, days_left FROM dpa_dashboard WHERE state IN ('EXPIRING','EXPIRED','MISSING')
    Send Emailto one named responsible person (<email>): "The contract with … needs attention: state …, days …". The mail password is kept in n8n "Credentials".

    The reminder is a signal. Renewal, review and signature are human.

  7. Limits

    • Every draft goes through a lawyer or a data protection officer before it is signed.
    • The model does not write clauses and does not decide whether someone is a "processor".
    • Do not put more personal data into the register than needed — contact names and email are enough.
    • Decisions about transfers outside the EU/EEA and about whether NIS2 applies are legal ones.

04Check

Quiz

1. In what form is a contract under Article 28 made (paragraph 9)?

2. A processor wants to engage another processor. What does Article 28(2) say?

3. How high can a fine under Article 83(4)(a) go?

4. What does the model fill in this lesson?

05What's next

06Sources

  1. EUR-Lex: Regulation (EU) 2016/679 (GDPR) — Article 28 and Article 83(4) (read on 03.10.2026).
  2. EUR-Lex: Directive (EU) 2022/2555 (NIS2) — Article 21(2).
  3. EUR-Lex: Implementing Decision (EU) 2021/915 · Implementing Decision (EU) 2021/914 — standard contractual clauses.
  4. Ollama: qwen2.5 🔒 local · structured outputs.
  5. n8n: Schedule Trigger · Postgres · Send Email.
  6. Jinja · HTTPX · PostgreSQL: CREATE TABLE.