Skip to content

Releases: Stoicera/einvoice_at

v0.2.0 — Wartung und Launch-Vorbereitung

Choose a tag to compare

@SebastianKrn SebastianKrn released this 09 Oct 09:07
v0.2.0
fc38f27

Live demo: https://einvoice.sebastiankern.net — the public validator needs no login.

Peppol BIS Billing 3.0 rule set 2026.5, a CVE dependency update (Spring Boot 4.1.1, Tomcat 11.0.26, Netty 4.2.19), anonymous uploads kept in memory, a ReDoS fix in the PII scrubber, and problem type URIs that resolve.

API-visible change: problem type URIs moved from https://einvoice-at.stoicera.com/problems/ (never resolved) to https://einvoice.sebastiankern.net/problems/; the slugs are unchanged. Configurable via PROBLEM_TYPE_BASE_URI, catalogue in docs/problems.md.

Full list: CHANGELOG.md.

What's Changed

  • docs: record the completion sprint — M6 closed on every item by @SebastianKrn in #24
  • feat(validation): adopt Peppol BIS Billing 3.0 rule set 2026.5 by @SebastianKrn in #26
  • fix(web): stop advertising the API docs on deployments that switch them off by @SebastianKrn in #27
  • feat(ops): copy database dumps off-site, and prove the copy is readable by @SebastianKrn in #25
  • fix(api): the OpenAPI description announced UBL support as future work it already shipped by @SebastianKrn in #28
  • fix(privacy): keep anonymous uploads in memory — the page promised they never touch disk by @SebastianKrn in #29
  • docs: publish API docs in production, record the session, and hand the next one a work queue by @SebastianKrn in #30
  • docs(worklog): the API docs are live, and the conditional links proved themselves in production by @SebastianKrn in #31
  • Abhängigkeiten: Spring Boot 4.1.x, Tomcat, Netty (CVE-Fix) by @SebastianKrn in #38
  • AGENTS.md: Non-goals aus PRD §5 und Rollback-Befehl eingetragen by @SebastianKrn in #39
  • AGENTS.md: Platzhalter durch echte Befehle ersetzt by @SebastianKrn in #37
  • Repo-Baseline: AGENTS.md, CLAUDE.md, Copilot-Anweisung by @SebastianKrn in #36
  • chore(backup): upstream the host backup scripts, record the armed off-site copy by @SebastianKrn in #35
  • ci: OWASP-Job auf 90 Minuten begrenzt by @SebastianKrn in #41
  • build(deps): bump phive-rules.version from 4.4.1 to 4.4.2 by @dependabot[bot] in #13
  • build(deps): bump org.glassfish.jaxb:jaxb-runtime from 4.0.5 to 4.0.9 by @dependabot[bot] in #16
  • build(deps): Bump eclipse-temurin from 201fbb8 to c42fecf by @dependabot[bot] in #33
  • build(deps-dev): bump com.diffplug.spotless:spotless-maven-plugin from 3.8.0 to 3.9.0 by @dependabot[bot] in #15
  • build(deps-dev): Bump com.tngtech.archunit:archunit from 1.4.2 to 1.5.0 by @dependabot[bot] in #32
  • build(deps): bump docker/login-action from 4.5.1 to 4.6.0 by @dependabot[bot] in #12
  • build(deps): bump actions/setup-java from 5.6.0 to 5.7.0 by @dependabot[bot] in #23
  • build(deps): Bump docker/setup-buildx-action from 4.2.0 to 4.3.0 by @dependabot[bot] in #34
  • fix(ai-assist): E-Mail-Muster im PiiScrubber begrenzt (ReDoS) by @SebastianKrn in #42
  • fix(api): Problem-Type-URIs auf auflösbaren Host, Basis konfigurierbar by @SebastianKrn in #43
  • docs: README mit Live-Demo, Community-Dateien, llms.txt, CHANGELOG 0.2.0 by @SebastianKrn in #44
  • Portfolio-Readiness: Checkliste vor dem öffentlichen Launch, Security-Kontakt repariert by @SebastianKrn in #40

New Contributors

Full Changelog: v0.1.0...v0.2.0

v0.1.0 — Austrian e-invoicing: generate, validate, convert

Choose a tag to compare

@SebastianKrn SebastianKrn released this 06 Aug 19:41
v0.1.0
a129d9f

Live demo: the public validator runs at https://einvoice.sebastiankern.net — no login needed; the dashboard behind Keycloak at /app.

The first release. A self-hostable platform that generates, validates and converts Austrian
e-invoices, with a German-language public validator, an authenticated dashboard, an optional AI
explanation per finding, and the operational scaffolding to run it.

Added — invoice model and formats

  • Canonical invoice model (core): the EN 16931 core subset as pure Java with zero
    dependencies, money as BigDecimal with explicit scale and rounding, Austrian VAT logic
    (20/13/10/0 %, reverse charge, exemption), and invariants checked at construction. Property-based
    tests (jqwik) for the arithmetic. (ADR-0003)
  • ebInterface 6.1 read and write (formats-ebinterface, wrapping ph-ebinterface), and the
    canonical ↔ ebInterface mapping in both directions.
  • Peppol BIS Billing 3.0 / UBL 2.1 read and write (formats-ubl, wrapping ph-ubl), including
    ubl:Invoice and ubl:CreditNote behind a sealed type, and both mapping directions.
    (ADR-0007)
  • Conversion between the two formats, always through the canonical model so the invoice is
    understood once, with a per-document report of exactly which fields the target could not carry.
  • PDF print view (rendering, Apache PDFBox): a German A4 invoice with the VAT breakdown as its
    own table, because § 11 UStG requires tax per rate. (ADR-0008)

Added — validation

  • Two pipelines behind one entry point. ebInterface: XSD → this project's own AT-B2G Schematron
    → hand-written business rules. UBL: the official OpenPeppol rule set, executed unmodified at a
    version pinned in code. (ADR-0004)
  • XXE-hardened parsing of every untrusted document, refusing a document merely for declaring a
    DOCTYPE, with an independent 20 MB module-level size guard.
  • German findings. Every finding carries a German message; English second. ~80 Peppol rules an
    Austrian filer realistically trips are translated from the rule set's own assertion texts.
  • A golden-file corpus of valid and systematically broken documents, one per rule.

Added — API, persistence and security

  • POST /invoices, POST /validate (anonymous, rate-limited, never stored),
    POST /convert, POST /reports/{id}/explain, DELETE /tenant, per-invoice ebinterface/ubl/
    pdf outputs, and tenant-scoped listings. RFC 9457 problem+json throughout; OpenAPI via springdoc
    with an off switch.
  • PostgreSQL + Flyway from V1, five tables, canonical JSON stored as JSONB and no XML ever
    persisted. (ADR-0005)
  • OAuth2 / Keycloak plus API keys, exactly one per request; keys stored as hashes only and
    refused the ability to manage keys or delete the tenant. Audit log of who did what and when,
    recording the payload's SHA-256 rather than the payload.
    (ADR-0006)
  • Rate limiting on the endpoints that cost CPU or money, with a different policy per endpoint and
    a stated reason for each difference.

Added — web UI and AI

  • Public validator (German-first, SEO meta, an explicit DSGVO notice) sharing one code path with
    the anonymous API so "an anonymous upload is never stored" has a single implementation.
  • Authenticated dashboard: invoice list and detail with all three downloads, report history, a
    four-step create-invoice wizard, API-key management and a Konto page. Server-rendered Thymeleaf
    with no CSS or JavaScript build step; every page works with JavaScript disabled.
    (ADR-0009)
  • AI explanations behind features.ai-explanations, off by default, degrading to a friendly
    notice when the provider is unreachable. PII is scrubbed before any call and no document fragment
    is ever sent. (ADR-0010, docs/privacy.md)

Added — GDPR

  • Art. 17 erasure: every invoice, report, key and audit event of the calling tenant, in one
    transaction, scoped by the credential so it cannot be aimed at anyone else.
  • Art. 5(1)(e) retention: a nightly purge of reports and audit events — and never invoices,
    because § 132 BAO obliges an Austrian business to keep them for seven years.
    (ADR-0011)

Added — operations (M6)

  • OpenTelemetry across the pipeline stages. Traces and metrics over OTLP; the validation stages
    reach the tracer through a plain-Java port so validation stays free of Spring. An
    observability compose profile brings Prometheus, Tempo and Grafana with datasources and a
    dashboard provisioned. (ADR-0012)
  • /actuator/info naming the commit and build time — and nothing about the people who wrote the
    code.
  • docs/deployment.md for Hetzner + Dokploy, plus scripts/backup.sh / scripts/restore.sh and
    an automated backup/restore drill that runs on every build.
  • SECURITY.md with a STRIDE-light threat model and a disclosure policy.
  • SERVER_FORWARD_HEADERS_STRATEGY so the rate limiter sees real client addresses behind a
    trusted proxy — and ignores forged ones without one. Behind a proxy the value is native, which
    reads the X-Forwarded-For chain from the end a proxy writes rather than the end a caller writes.
    All three directions tested.
  • Instance election for the retention job via a PostgreSQL advisory lock, so several replicas
    against one database no longer all purge.
  • A real authorization-code login driven through Keycloak's own form in a browser test, closing
    the gap M5 recorded by name.
  • Lighthouse ≥ 95 asserted in CI on both public pages; measured 100 in all four categories.
  • CI publishes to GHCR and triggers a Dokploy deploy webhook, both skipping loudly without their
    secrets.

Known limits

Stated here as well as in SECURITY.md, because a release note that lists only
successes is marketing:

  • The rate limiter is in-memory and per instance; two replicas mean two allowances.
  • An Art. 17 erasure deletes the audit trail with the rest of the tenant's data — the alternative is
    not honouring the request.
  • Logout is local: it ends this application's session, not the Keycloak SSO session.
  • Peppol finding messages are English where the rule set publishes only English; the German message
    is a German frame around the official wording, except for the ~80 translated rules.
  • This platform is not a certified Peppol Access Point and sends invoices nowhere.
  • No ZUGFeRD/Factur-X hybrid PDF.