EVIDENT

A real analysis

Read the output before you install anything

Five artefacts from an actual run of the demonstration project that ships with EVIDENT. Not a mock-up and not a screenshot: the exports, as the product produced them.

Captured from build 2026-09-09 · 86365ef9_run_9152b8cac23440148135b5ea30eeace7

Everything in them is invented. Nébula Shop is a fictional shop written so this product has something real to read, and it contains on purpose the awkward things a real system has — a table whose name does not match its class, a national identifier, a special category hiding in an ordinary-looking column, two log calls that hand a whole customer to a logger, and references pointing at rows that were deleted.

Why does it say that? — one answer, in full

The reports below are worth reading only if their claims can be questioned. This is what the product returns when somebody asks about one field of the demonstration: eight steps, in the order they were reached, across all five kinds of fact. It came out of the same endpoint the interface calls, and it is printed here unedited.

main.customers.dni

Captured from build run_9152b8cac23440148135b5ea30eeace7

WHY?

  1. OBSERVED

    Property dni of Customer was read from the repository

    maps to dni

  2. OBSERVED

    Column dni of customers was read from the database

  3. INFERRED

    Customer.dni maps to main.customers.dni.

    Confidence 1.00. NAMING_CONVENTION

  4. DECLARED

    Lawful basis: Performance of a contract

    Marketing consent is held separately and is not what this covers.

  5. DECLARED

    Purpose: Account management, order fulfilment and transactional messages

  6. DECLARED

    Recipient: The delivery carrier, for the name, the address and the telephone number only

  7. DOCUMENTARY

    Retention: 24 months after the account is closed

  8. DECIDED

    Confirmed by a reviewer on 2026-09-09.

    No reason was recorded.

Read the order. Two things read from systems, one conclusion drawn by a rule from what was read, three facts the organisation states about itself, one carried in a controlled document, and a person’s decision at the end. Nothing in the middle is presented as observation, and the last step is the only one with a name against it.

What each field is, before any report is written

Every column in the demonstration carries two answers, kept apart. What the regulation calls it — personal data, an Article 9 special category, Article 10 conviction data, or not established — and how much attention EVIDENT suggests it needs. The second is this product’s own ordering and says so: the regulation defines no such scale.

Three columns here are worth opening. staff_records.health_notes is a special category, and what establishes it is not the name — "health notes" can be many things — but an employee number, a full name and an email address in the same table. staff_records.criminal_record_check is Article 10, a different regime with a different lawful basis, sitting two rows away at the same sensitivity: the clearest way to see that the level and the category are different questions. devices.device_fingerprint is neither. The name reaches for something Article 9 covers, nothing corroborates it, so the category stays not established and the row asks for a person.

And warehouse_slots.orientation is the direction a shelf faces. It matches the word the regulation uses for sexual orientation, the columns around it argue that reading out, and it appears with no category and no citation — which is the result, not an omission.

The staff table in EVIDENT, with the classification panel open on the health notes column
The panel behind the dot: the legal category and the sensitivity as separate statements, the rules that fired, both versions, what the conclusion rests on, and the articles — each linked to the official text.

The record of processing activities

What personal data the system holds and where, with what the organisation has stated about each activity. Read the purpose column: two of the five activities have one, three do not, and the report says so rather than leaving the cell empty.

Article 30 inventory, drawn from the fields this analysis assessed

FieldValue
SystemAssessment
Analysis run86365ef9_run_9152b8cac23440148135b5ea30eeace7
Exported2026-09-09

What this report cannot say

  • A purpose, a legal basis, a retention period and a recipient are decisions an organisation made, not facts a schema carries. They are read here from what this project has stated, and every stated fact is printed with the class of evidence it is: declared where somebody said so, documented where a controlled document backs it.
  • 5 of 7 processing activities have no recorded purpose. They are listed with the purpose not recorded rather than left blank, because a blank in this column reads as nothing to report.
  • 41 fields are neither confirmed nor ruled out as personal data, and listed as unknown rather than counted as clean, because an Article 30 record built on a silent assumption is one nobody can defend.

At a glance

MeasureValue
Personal data fields16
Special category1
With a stated purpose2/7
Processing activities7
Unclassified41

Processing activities

One entry per object holding personal data. The purpose, the legal basis and the retention period are decisions an organisation records; this is the inventory they attach to.

Read from the systems, concluded from what was read, stated by the organisation and carried in a controlled document, and some of it never established.

ObjectSeverityFieldsData subjectsPurposeLegal basisRetention
main.customer_addressesHighpostcode, streetCUSTOMERDelivering an order to the address the customer gave (declared)Not recordedNot recorded
main.customersHighbirth_date, dietary_preference, dni, email, full_name, phoneCUSTOMERAccount management, order fulfilment and transactional messages (declared)Performance of a contract (declared)24 months after the account is closed (documented)
main.devicesHighdevice_fingerprintSubject type not recordedNot recordedNot recordedNot recorded
main.legacy_subscribersHighemailCUSTOMERNot recordedNot recordedNot recorded
main.paymentsHighcard_last4Subject type not recordedNot recordedNot recordedNot recorded
main.staff_recordsCriticalcriminal_record_check, email, full_name, health_notesCUSTOMERNot recordedNot recordedNot recorded
main.support_ticketsHighbodySubject type not recordedNot recordedNot recordedNot recorded

Fields

Every field not ruled out as personal data, most sensitive first. Unknown is listed rather than counted as clean.

Read from the systems and concluded from what was read.

FieldSeverityLegal categorySensitivityRests onData subjects
main.staff_records.health_notesCriticalSpecial categoryHighContextNot recorded
main.customer_addresses.postcodeHighPersonal dataStandardName onlyCUSTOMER
main.customer_addresses.streetHighUnknownUnknownNoneCUSTOMER
main.customers.birth_dateHighPersonal dataStandardName onlyCUSTOMER
main.customers.dietary_preferenceHighUnknownUnknownNoneNot recorded
main.customers.dniHighPersonal dataElevatedName onlyCUSTOMER
main.customers.emailHighPersonal dataStandardName onlyCUSTOMER
main.customers.full_nameHighPersonal dataStandardName onlyCUSTOMER
main.customers.phoneHighPersonal dataStandardName onlyCUSTOMER
main.devices.device_fingerprintHighPossibly Special categoryHighName only · needs reviewNot recorded
main.legacy_subscribers.emailHighPersonal dataStandardName onlyCUSTOMER
main.payments.card_last4HighUnknownUnknownNoneNot recorded
main.staff_records.criminal_record_checkHighCriminal convictions and offencesHighSelf corroborating termNot recorded
main.staff_records.emailHighPersonal dataStandardName onlyCUSTOMER
main.staff_records.full_nameHighPersonal dataStandardName onlyCUSTOMER
main.support_tickets.bodyHighUnknownElevatedNone · needs reviewNot recorded
main.activity.latitudeModeratePersonal dataElevatedName onlyNot recorded
main.activity.locationModerateUnknownUnknownNoneNot recorded
main.activity.longitudeModeratePersonal dataElevatedName onlyNot recorded
main.activity.occurred_atModerateUnknownUnknownNoneNot recorded
main.catalogue_items.activeModerateUnknownUnknownNoneNot recorded
main.catalogue_items.descriptionModerateUnknownElevatedNone · needs reviewNot recorded
main.catalogue_items.nameModerateUnknownUnknownNoneNot recorded
main.catalogue_items.price_centsModerateUnknownUnknownNoneNot recorded
main.catalogue_items.stockModerateUnknownUnknownNoneNot recorded
main.customer_addresses.cityModerateUnknownUnknownNoneNot recorded
main.customer_addresses.countryModerateUnknownUnknownNoneNot recorded
main.customer_addresses.delivery_notesModerateUnknownElevatedNone · needs reviewNot recorded
main.customers.created_atModeratePersonal dataStandardName onlyNot recorded
main.customers.marketing_opt_inModerateUnknownUnknownNoneNot recorded
main.customers.postal_codeModerateUnknownUnknownNoneNot recorded
main.devices.first_seen_atModerateUnknownUnknownNoneNot recorded
main.devices.platformModerateUnknownUnknownNoneNot recorded
main.legacy_subscribers.sourceModerateUnknownUnknownNoneNot recorded
main.legacy_subscribers.subscribed_atModerateUnknownUnknownNoneNot recorded
main.order_lines.quantityModerateUnknownUnknownNoneNot recorded
main.order_lines.unit_price_centsModerateUnknownUnknownNoneNot recorded
main.orders.placed_atModerateUnknownUnknownNoneNot recorded
main.orders.statusModerateUnknownUnknownNoneNot recorded
main.orders.total_centsModerateUnknownUnknownNoneNot recorded
main.payments.authorised_atModerateNot personalStandardName onlyNot recorded
main.payments.methodModerateUnknownUnknownNoneNot recorded
main.payments.statusModerateUnknownUnknownNoneNot recorded
main.staff_records.employee_numberModerateUnknownUnknownNoneNot recorded
main.staff_records.started_atModerateUnknownUnknownNoneNot recorded
main.support_tickets.created_atModeratePersonal dataStandardName onlyNot recorded
main.support_tickets.statusModerateUnknownUnknownNoneNot recorded
main.support_tickets.subjectModerateUnknownUnknownNoneNot recorded
main.warehouse_slots.aisleModerateUnknownUnknownNoneNot recorded
main.warehouse_slots.capacityModerateUnknownUnknownNoneNot recorded
main.warehouse_slots.depthModerateUnknownUnknownNoneNot recorded
main.warehouse_slots.orientationModerateUnknownUnknownNoneNot recorded
main.warehouse_slots.position_xModerateUnknownUnknownNoneNot recorded
main.warehouse_slots.position_yModerateUnknownUnknownNoneNot recorded
main.warehouse_slots.position_zModerateUnknownUnknownNoneNot recorded
main.warehouse_slots.secretModerateNot personalElevatedName onlyNot recorded
main.warehouse_slots.updated_atModeratePersonal dataStandardName onlyNot recorded

The risk register, including the risk that is in no column

A date of birth is moderate on its own and a postcode is low on its own. Together in one table they identify most of the people in it, and no scanner that looks at columns one at a time can say so. Every column named in that entry was classified before the rule was allowed to speak about it.

What was observed, with the evidence that raised it

FieldValue
SystemAssessment
Analysis run86365ef9_run_9152b8cac23440148135b5ea30eeace7
Exported2026-09-09

What this report cannot say

  • Likelihood, impact, an owner and a treatment decision are judgements a person makes. This register is what they are made against: every entry carries the evidence that raised it and nothing that nobody recorded.
  • Inherent risk here is a band, not a score. Two entries in the same band are the same finding, and ordering them against each other would be arithmetic on an opinion.

At a glance

MeasureValue
Entries1
Critical1
Treated0

Risk from combinations

Risk that is in no column and is in the combination of several. A date of birth is moderate on its own and a postcode is low on its own; together in one table they identify most of the people in it. Every column named here was classified before this rule was allowed to say anything about it.

Read from the systems and concluded from what was read.

ScenarioSeverityEvidenceInherent riskTreatment
main.customers holds enough to identify a person without naming oneHighmain.customers.birth_date, main.customers.postal_codeHIGHNot recorded
main.devices links a person to where they wentHighmain.devices.device_fingerprint, main.activity.latitude, main.activity.longitude, main.activity.locationHIGHNot recorded

Register

Ordered by inherent risk. Likelihood, impact, an owner and a treatment are judgements a person makes, and are recorded here as absent until somebody makes them. There is no review queue behind this register yet: its entries are derived each time the report is built, so nothing survives from one run to the next for a decision to attach to.

Read from the systems and concluded from what was read.

ScenarioSeverityCategoryEvidenceInherent riskExisting controlsTreatment
main.staff_records.health_notes holds special-category dataCriticalComplianceProtected in its own right, which changes what the table around it requires.HighNone recordedNot recorded

Logging exposure

The findings nothing else in this category produces: the log calls that hand a whole customer to a logger, named down to the file and the line.

Where the application writes personal data, or a credential, into a log

FieldValue
SystemAssessment
Analysis run86365ef9_run_9152b8cac23440148135b5ea30eeace7
Exported2026-09-09

What this report cannot say

  • Nothing here decides what is sensitive. Every finding rests on a classification made elsewhere — by a rule, and in the best case confirmed by a person — and the table below says which. A finding resting on an unconfirmed proposal is worth checking before it is worth acting on.
  • This reads the code that writes the logs, not the logs themselves. It establishes that an application would write a field to a log when that line runs; it does not establish that the line has ever run, or what is in any log file today.
  • A call this reader did not recognise is a call it says nothing about. It finds a logger by the conventional names — log, logger, logging, console — so an application that wraps its logger in something named otherwise is a gap here rather than a clean result.
  • A field named in a log call is retained for as long as the log is, which is almost never the retention this system’s record of processing declares, and it cannot be erased on request because nobody rewrites log archives.

At a glance

MeasureValue
Log calls at risk2
Entities logged whole2
Credentials in log calls0
Fields reaching a log6
Resting on a decision0/2

Credentials named in a log call

A secret written to a log is in every copy of that log, including the ones already shipped to whoever aggregates them. Removing the call is half of the remedy and rotating the value is the other half.

Read from the systems.

WhereLevelNamesSeverity
No log call names a credential

Entities handed whole to a logger

These write every field the class holds, and will write the next one somebody adds to it without anybody revisiting the line. They are the ones worth changing first.

Read from the systems.

WhereSeverityLevelWrites to the logClassification
api/src/services/customers.service.ts:34CriticalinfoCustomer.email, Customer.fullName, Customer.phone, Customer.dni, Customer.birthDate, Customer.dietaryPreferenceProposed by a rule
api/src/services/orders.service.ts:33CriticalinfoCustomer.email, Customer.fullName, Customer.phone, Customer.dni, Customer.birthDate, Customer.dietaryPreferenceProposed by a rule

Classified fields written to a log

The call names a field the assessment says holds personal data, and the thing that holds it. A field in a log is retained for as long as the log is.

Read from the systems and concluded from what was read.

WhereLevelWrites to the logClassification
No call writes a classified field to a log

About this check

  • 2 calls hand a whole entity to a logger. Those are the ones worth changing first: they write every field the class holds today and every field somebody adds to it next month, without anybody revisiting the line.
  • None of these rests on a classification a person has confirmed. They are findings about fields a rule proposed as personal, which is a good reason to look and a poor reason to act before looking.

Coverage, and what could not be seen

The other half of every report. How much of the assessment is settled, how much is waiting for a person, and what the analysis could not reach at all.

What was analysed, what was not, and why

FieldValue
SystemAssessment
Analysis run86365ef9_run_9152b8cac23440148135b5ea30eeace7
Exported2026-09-09

What this report cannot say

  • Coverage is measured against what this analysis could see. A schema the credential cannot read and a repository path nobody configured are both absent from the denominator, which is why the stages that ran are listed beside the figure.
  • An element the application declares it does not store is not a gap. Counting those against coverage produces a number that says fifteen per cent about a model that is almost entirely mapped.

At a glance

MeasureValue
Database coverage76%
Fields assessed79
Waiting on a person2
Never examined0

What did not need a person

What this analysis settled on its own, and what it handed to somebody. The question asked of every field is whether it holds personal data: one the classifier answered, either way, reached nobody. One it left open did, and so did a personal field whose special category is still undecided. It says what this analysis did not have to ask, and it is not a claim about how much review this organisation does.

Read from the systems and concluded from what was read.

MeasureValue
Fields analysed79
Settled by evidence18
Need human judgement61
Review avoided23%

What ran

A stage that did not run is a gap in every figure this platform publishes, including the ones that look complete.

Read from the systems.

StageStatusReported

What the evidence reached

The distribution is the shape of the assessment.

MeasureCount
CONFIRMED61
NOT_PERSISTED11
INSUFFICIENT_EVIDENCE19

Why elements went unexamined

"We did not look" is only useful if it says what would have to change for the answer to become available.

Concluded from what was read.

ReasonElements
Nothing went unexamined0