GDPR by design, and every claim tested

An address is personal data. This page lists exactly what Addrova logs, stores and never keeps. Each statement has an identifier and is tied to an automated test that runs on every change of the code.

See the technical docs

The zero-log claims

In these tables, "address" means any customer input: free text, components, requested coordinates, batch files. Source of truth: the zero-log specification of the API, in French; this page translates it without changing its meaning.

Specification version of 2026-10-19

What is logged (exhaustive list)

IdStatementAutomated test
ZL-01One line per HTTP request, with exactly: method, route template (/v1/geocode, never the real URL or the query string), status, duration, pseudonymised tenant, random request id. No IP address, no user-agent, no API key.api/tests/test_zerolog.py::test_access_log_exact_fields
ZL-02The only other messages emitted by Addrova's code are those in the inventory below; their parameters are job identifiers, counters, statuses or exception type names.api/tests/test_privacy_claims.py::test_log_inventory_matches_code
ZL-03The tenant is pseudonymised everywhere (logs, traces): 16 hexadecimal characters of a salted SHA-256 of its identifier; the plain identifier never appears.api/tests/test_observability.py::test_tenant_hash_is_stable_and_opaqueapi/tests/test_zerolog.py::test_access_log_exact_fields

What is never logged nor traced

IdStatementAutomated test
ZL-10No address, no requested coordinate and no API key appears in the logs, on every route (geocoding, reverse, autocomplete, repair, batch, Google and Nominatim compatibility), including on error (engine down, rejected input, invalid parameter, wrong key, unknown route).api/tests/test_zerolog.py::test_canary_never_logged
ZL-11Third-party library messages are neutralised: parameters masked, query string erased, exception reduced to its type (no message, no trace).api/tests/test_zerolog.py::test_third_party_logger_args_and_query_redacted
ZL-12Error responses do not return the offending value nor the engine's internal message.api/tests/test_api.py::test_geocode_invalid_paramsapi/tests/test_api.py::test_engine_errors_do_not_echo_query
ZL-13OpenTelemetry traces carry only allow-listed attributes (method, route template, status, pseudonymised tenant, engine operation, country, result count, precision of the first result), with no events; no address, no coordinate, no key, no plain tenant identifier.api/tests/test_observability.py::test_spans_carry_only_allowlisted_attributesapi/tests/test_observability.py::test_span_content
ZL-15Console: the email address, magic-link tokens and session tokens appear in no log.api/tests/test_console.py::test_email_and_tokens_never_logged
ZL-16Public site demo (/demo/v1): neither the entered address nor the visitor's IP address appears in the logs; the IP is never stored, only an HMAC fingerprint with a daily secret (unusable after rotation) is used to count requests.api/tests/test_demo.py::test_demo_never_logs_address_or_ipapi/tests/test_demo.py::test_fingerprint_rotates_daily_and_hides_ipapi/tests/test_demo.py::test_redis_mode_shares_secret_and_counters
ZL-14Prometheus metrics only have bounded label values; an unknown path is counted as <unmatched>.api/tests/test_observability.py::test_metrics_labels_are_boundedapi/tests/test_observability.py::test_spans_carry_only_allowlisted_attributes

What is stored, and for how long

IdStatementAutomated test
ZL-20API keys are stored only as a SHA-256 fingerprint (plus 12 prefix characters for display).api/tests/test_tenancy_persistent.py::test_keys_are_hashed_and_revocable
ZL-21The database holds no request and no result: only tenants, keys (fingerprints) and usage counters (tenant, day, endpoint, count).api/tests/test_privacy_claims.py::test_schema_has_no_content_columns
ZL-22Batch: the input file is deleted as soon as processing ends; the result and the job metadata are deleted at the end of the retention period (1 hour after completion by default, configurable) or immediately on DELETE; a job never processed is purged after 24 hours.api/tests/test_batch.py::test_csv_roundtrip_usage_and_input_purgeapi/tests/test_batch.py::test_isolation_delete_and_expiryapi/tests/test_batch.py::test_result_purged_after_retention
ZL-23Batch job metadata contains no row of the file (counters, dates, column names, default country).api/tests/test_privacy_claims.py::test_batch_metadata_has_no_row_content
ZL-25Console: the only personal data stored is the account's email address (and that of a pending sign-in link, valid 15 minutes, single use); link and session tokens are stored only as a SHA-256 fingerprint.api/tests/test_privacy_claims.py::test_schema_has_no_content_columnsapi/tests/test_console.py::test_link_is_single_use_and_logout
ZL-26An organization sees neither the keys nor the usage of another; a revoked or expired (rotated) key is refused.api/tests/test_console.py::test_org_isolationapi/tests/test_console.py::test_keys_lifecycle_and_plan_limit
ZL-27Batch-completion webhook: the payload contains only the job's identifier, status, counters and links, never a row of the file nor an address; the webhook's URL and secret are never logged.api/tests/test_batch_stream.py::test_webhook_delivery_signed_without_addresses_and_retried
ZL-28Idempotency (Idempotency-Key): only a SHA-256 fingerprint of the request (and, for a batch, the job identifier) is kept for 24 hours, never the address sent nor the response.api/tests/test_idempotency_versioning.py::test_idempotency_never_stores_addressapi/tests/test_idempotency_versioning.py::test_in_progress_conflict_and_stores_keep_no_content
ZL-29No undeclared network egress: deny by default in the namespace; the website has no egress; the API only reaches the cluster DNS, its databases (PostgreSQL, Redis), the internal OTel collector and, for webhooks, HTTPS to the public Internet (private, local and metadata ranges excluded). No third-party analytics tool can receive a request.api/tests/test_k8s_hardening.py::test_egress_is_limited_to_declared_flowsapi/tests/test_k8s_hardening.py::test_namespace_is_restricted_and_denies_by_default
ZL-24One customer's batch job is invisible to other customers.api/tests/test_batch.py::test_isolation_delete_and_expiry

What does not exist (yet)

IdStatementAutomated test
ZL-30The asynchronous fallback of /v1/repair (LLM) is disabled: no external call, no address leaves the service.api/tests/test_repair.py::test_repair_unrepairable_and_async_disabled
ZL-32No email is actually sent in v0 (in-memory outbox) and no real billing provider is connected (dummy provider).api/tests/test_privacy_claims.py::test_no_external_transport_by_default
ZL-31No result cache is in place in v0 (the rotating-secret HMAC cache of ADR 0005 is not implemented yet).api/tests/test_privacy_claims.py::test_no_result_cache_in_v0

Log message inventory

These are the only messages the code emits, in their exact format. A test fails if the code and this list diverge.

%s %s %d %.1fms tenant=%s rid=%s
job %s queued rows=%d
job %s %s rows=%d matched=%d
job %s failed: %s
engine loaded: %s
addrova_engine not installed, using the fake engine
usage flush failed: %s
index preloaded: %d bytes
webhook job %s attempt %d status %s
engine contract violation: candidate without source
engine failure: %s

Outside the scope of these tests

These points are checked another way, and we say so:

  • Ingress / proxy: its access log must be disabled on the API routes (ADR 0004); infrastructure configuration, to be checked in infra/ and on the cluster.
  • Engine (addrova_engine): it commits not to log requests (ADR 0002); its own tests check it.
  • Trace export: only if ADDROVA_OTLP_ENDPOINT is set, to a collector hosted in the EU; without this setting, no trace leaves the process.
  • Batch file storage: v0 on the container's local disk (permissions 0600); encrypted EU object storage planned for production (ADR 0006, ADR 0009).

Because nothing is kept, nothing is reused

Addrova does not use your requests to train models, build datasets or enrich its index, and the code makes that concrete: queries and results are never logged (ZL-10), the database holds no request nor result (ZL-21), batch files are deleted after processing (ZL-22), there is no result cache (ZL-31) and no address is sent to an external service (ZL-30).

Not claimed yet

  • EU-only hosting: the hosting provider and region are not published yet. [À VÉRIFIER]
  • Sub-processors and Data Processing Agreement (DPA): to be published, with online signature, before launch. [À VÉRIFIER]
  • Certifications: none claimed. ISO 27001 is planned for a later stage.

Data sources and licences

We rely on official open data under open licences (for France, Licence Ouverte 2.0). Every API response carries an attribution array naming its sources. Data under share-alike licences, such as OpenStreetMap, lives in a separate index and is never merged with the official one.

Compliance questions

Can I verify these claims?

Each claim names the automated test that checks it. The tests run on every change; a test fails if one of them disappears or if the log inventory no longer matches the code. Access to the test suite for audits is to be defined. [À VÉRIFIER]

Do you log my IP address?

The application does not: its access log has no IP address, no user-agent and no API key (ZL-01). The ingress in front of it must also have its access log disabled; that is an infrastructure setting, listed above as outside the scope of the tests.

How long are batch results kept?

One hour after completion by default, or until you delete the job (ZL-22). The input file is deleted as soon as processing ends.

Related guides