Skip to content
Francesco Bilotta
Compliance as code

Implementing the EU AI Act in Java: A Code-First Approach

Compliance is architecture, not a Word document. Building the Annex IV file, the Article 12 audit log, and Article 14 oversight in code.

Francesco Bilotta · · 4 min read

The standard way a company meets a new regulation is to hire someone to read it and produce a document. A consultant studies the text, writes a technical file in Word, exports a PDF, and everyone signs it. The problem with that PDF is that it is stale the day it is signed. The system keeps changing after the signature. The model gets retrained, an endpoint moves, a logging call gets refactored out, and the document describes a system that no longer exists. The audit trail was never connected to the running code, so it drifts the moment the code does.

I do not believe compliance is a document. I believe compliance is a structural property of the system, and a property you can check at build time. So when I needed EU AI Act controls in production, I did not write a technical file. I annotated the domain types and let the build generate the evidence. That is what spring-aiact is: an open-source library, Apache 2.0, that does EU AI Act compliance-by-annotation for Spring Boot.

Read the regulation, not the summary

Before writing a line of it I read the actual text of the AI Act, not a third-party interpretation of it. This matters, because the obligations are precise and the summaries blur them. The Act classifies certain systems as high-risk, and it is that classification that triggers the heavy duties. A high-risk AI system has to keep records automatically over its lifetime under Article 12. It has to carry a technical file with the contents laid out in Annex IV. It has to allow effective human oversight under Article 14. And it has to be covered by a declaration of conformity under Article 47.

Those are not four documents to write once. They are four properties the running system either has or does not have. A code-first approach asks: which of these can the compiler and the runtime prove for me, instead of a human asserting them in prose?

Annotate the domain, generate the evidence

The unit of compliance is the domain type. You mark a class as part of a high-risk system, and the library takes it from there.

@HighRiskSystem(
    name = "credit-scoring",
    annexIII = "5(b)"        // Annex III category, illustrative
)
public class CreditDecision {

    @AuditLog(article = 12)   // this operation is recorded, tamper-evident
    public Outcome score(Applicant applicant) {
        ...
    }

    @HumanOversight(article = 14)  // decision routed to a reviewer before it binds
    public void confirm(Outcome outcome, Reviewer reviewer) {
        ...
    }
}

The annotations are not decoration. They are read by annotation processors at compile time, so the evidence is produced by the build, not by a person after the fact. The processor sees @HighRiskSystem and assembles the Annex IV technical file from what is actually in the code: the system, its components, the operations it logs, the oversight it wires up. It emits the Article 47 declaration of conformity from the same source. If a high-risk type is missing a control the regulation requires, that is a build-time signal, not a finding a consultant surfaces months later. The evidence and the system are generated from one source, so they cannot drift apart.

The Article 12 record-keeping is the part that has to survive scrutiny. An audit log is worthless if someone can quietly edit a row after the fact. So the log is tamper-evident: each entry is chained to the previous one with an HMAC, the way a ledger is chained. Break or edit any link and the chain no longer verifies. That gives you record-keeping over the system's lifetime that you can actually stand behind, because the integrity is a property of the data structure, not a promise in a policy.

The audit trail is a side effect

There is a companion library for GDPR built on the same idea, and an end-to-end demo, spring-gdpr-aiact-demo, that runs both of them together on Spring Boot. It exists because I wanted to prove the whole thing works in a real application, not in a slide. You annotate the domain, you run the build, and out come the technical file, the conformity declaration, and a verifiable audit log.

That is the whole thesis. The audit trail is a side effect of building the system correctly, not a document you write afterwards to describe a system you built some other way. When compliance lives in the types, the evidence stays true by maintenance: change the code and the evidence changes with it, or the build tells you that you have broken a control. The obligations stop being something you attest to on paper and become something the compiler checks.

That is the difference between compliance as a Word document and compliance as architecture. One is a snapshot that starts decaying at signature. The other is a property of the system that holds as long as the system runs.