Skip to main content

kvkk

CI PyPI Python License: MIT

Find and mask Turkish personal data — TCKN, IBAN, VKN, payment cards, phone numbers, licence plates — in files, logs and database dumps. Checksum-verified, so it does not cry wolf. No network, no telemetry, no dependencies beyond trkit and typer. Ekran görüntüsü 2026-08-11 185938

pip install kvkk
kvkk scan .

Why

Personal data does not leak out of databases. It leaks out of the places nobody thinks of as a database:

[2026-08-11 09:14:02] INFO  order.create  user=10000000146 iban=TR330006100519786457841326

That line is in a log file, in a .env, in a fixture, in a CSV someone mailed themselves, in the JSON body of a bug report. Under KVKK it is personal data wherever it sits — and nobody knows how many copies exist, because nobody has ever counted.

kvkk counts them.

$ kvkk scan .
fixtures/customers.csv
  ! 2:16  tckn  1;Ahmet Yılmaz;100******46;053*********67;İzmir
  ? 2:28  phone  1;Ahmet Yılmaz;100******46;053*********67;İzmir

logs/app.log
  ! 2:49  tckn  …order.create   user=100******46 iban=TR3*******************26 t…
  ! 2:66  iban  …create   user=100******46 iban=TR3*******************26 total=1249.90
  ! 3:49  credit_card  …DEBUG payment.charge card=411**************11 exp=12/28
  ! 4:47  phone  …INFO  sms.send       to=+90************67 template=order_ok
  ! 7:49  vkn  …INFO  invoice.issue  VKN: 454*****20 amount=1249.90

118 findings in 6 of 412 files
  94 high  21 medium  3 low
  tckn ×61, iban ×33, phone ×21, email ×3

Note what is not in that list: order_id=12345678901 on line 5 of the same log. Eleven digits, right shape, fails the checksum — so it is not a finding.

It does not cry wolf

A scanner that flags every eleven-digit number gets removed from CI on day two. Three rules keep this one quiet enough to leave switched on:

Checksums beat regular expressions. TCKN, IBAN, VKN and card numbers all carry check digits. A value that fails its checksum is never reported, so an order number does not become an incident:

$ kvkk check 12345678901
not recognised as personal data

$ kvkk check 10000000146
Turkish national ID (TCKN)  [high]  10000000146

Ambiguous shapes need context. A tax ID has a single check digit, so about one in ten arbitrary ten-digit numbers passes it. vkn is therefore only reported when a nearby word — VKN, vergi no, tax id — agrees with the reading.

Everything else is graded, not dropped. +90 532 … is strong evidence; seven loose digits are not. Both are reported, at different confidence levels, and --min-confidence decides what you want to see.

detector verified by confidence
tckn checksum high
iban ISO 13616 mod-97 high
credit_card Luhn high
vkn checksum + context high
phone pattern + prefix high / medium / low
email pattern medium
plate pattern medium
ip pattern low

The report is not the second leak

Every excerpt is masked before it is written, in every format. A kvkk report can be pasted into a ticket, attached to an e-mail, or committed to a repo without becoming another copy of the data it is warning you about. There is no flag to turn that off.

kvkk scan . --html report.html    # one self-contained file, no assets, no scripts
kvkk scan . --json                # for the pipeline

Some valid identifiers belong to nobody

Every codebase has test fixtures and documentation examples with real check digits — the 4111… Visa test card, an IBAN copied out of a spec. A scanner that cannot be told about them is a scanner that gets switched off in week one.

A .kvkkignore file at the root of the scan, globs one per line:

tests
fixtures/seed.sql
src/generated

Or a marker on a single line. The comment character does not matter, so the same spelling works in Python, SQL, YAML and JavaScript:

CUSTOMER_FIXTURE = "10000000146"  # kvkk: ignore
-- kvkk: ignore-file
INSERT INTO customers VALUES ('10000000146', ...);

kvkk applies this to itself: its own .kvkkignore covers the test suite and the docstrings, and its CI runs kvkk scan . --fail-on high on every push. --no-ignore-file sees through all of it.

Four ways to mask, for four different jobs

KVKK draws a hard line between anonymisation — the link to a person is destroyed — and pseudonymisation, where it is only hidden and the result is still regulated personal data. Which side of that line you need depends on what you are doing with the file.

$ kvkk mask app.log --strategy partial   # 100******46   — support can still confirm it
$ kvkk mask app.log --strategy redact    # [TCKN]        — the value is gone
$ kvkk mask app.log --strategy hash      # tckn_8f2a1c   — same person, same token
$ kvkk mask app.log --strategy fake      # 29874500146   — a *valid* fake, for staging

hash and fake are deterministic and salted:

kvkk mask dump.sql --strategy fake --salt "$KVKK_SALT" -o staging.sql

The same input and salt always give the same output, so foreign keys still join across tables and files. fake output passes the real validators — a faked TCKN satisfies the TCKN checksum, a faked IBAN satisfies mod-97 — so a masked staging database does not fail its own input validation. Change the salt and the mapping is gone for good.

In CI

scan exits 1 when it finds something at or above --fail-on, so a leak fails the build instead of reaching production:

- run: pip install kvkk
- run: kvkk scan . --fail-on high

As a pre-commit hook:

repos:
  - repo: local
    hooks:
      - id: kvkk
        name: kvkk
        entry: kvkk scan --fail-on high
        language: system
        pass_filenames: false

Library usage

from kvkk import detect, mask_text, scan_path, render_html

detect("müşteri 10000000146")
# [Match(kind='tckn', value='10000000146', start=8, end=19, confidence=HIGH)]

mask_text("müşteri 10000000146")[0]
# 'müşteri 100******46'

result = scan_path(Path("logs"))
result.by_kind()  # {'tckn': 61, 'iban': 33}
result.worst()  # Confidence.HIGH
Path("report.html").write_text(render_html(result), encoding="utf-8")

Scanning is streamed line by line, so a ten-gigabyte log file is scanned in constant memory.

Command line

kvkk scan [PATHS]     report personal data found in files or directories
kvkk mask [FILE]      mask a file or stdin
kvkk check VALUE      say what a single value is, and whether it checks out
kvkk kinds            list every detector

Useful flags: --only tckn,iban, --min-confidence high, --fail-on high, --exclude '*.sql', --html FILE, --json, --limit N, --no-ignore-file.

What this does not do

Being honest about the edges is part of the tool:

  • It does not find names or addresses. Those have no checksum and no fixed shape, and guessing at them produces the kind of noise that gets a scanner switched off. Structured identifiers only.
  • A checksum is not a person. 10000000146 is a valid TCKN; it is not necessarily anyone's. Findings are evidence to review, not proof of a breach.
  • It does not read binaries, images or PDFs. No OCR, no archive extraction.
  • It is not legal advice. It tells you where the data is. What you are obliged to do about it is a question for a lawyer.

Development

git clone https://github.com/ardazeybek-dev/kvkk
cd kvkk
pip install -e ".[dev]"
pytest
ruff check .

Licence

MIT — see LICENSE.

Metadata

Release files for kvkk 0.1.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for kvkk 0.1.2
File Size Uploaded
kvkk-0.1.2.tar.gz 29.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for kvkk 0.1.2
File Interpreter ABI Platform
kvkk-0.1.2-py3-none-any.whl Python 3 none any Details

Total release size: 57.0 kB

Release files / kvkk-0.1.2.tar.gz

Download URL kvkk-0.1.2.tar.gz
Size 29.9 kB
Tags Source
SHA-256 checksum
How to use checksums
a6d1bbb11e935f802c2e541a109d741e7d0bf3828cbd6d7d214ce3e5cb610cea
BLAKE2b-256 checksum
How to use checksums
f28f469949b39389994ba9ed35c0651998b139d3b8f5e648bb43ec249c9ce4e1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 11, 2026.

Transparency log

Release files / kvkk-0.1.2-py3-none-any.whl

Download URL kvkk-0.1.2-py3-none-any.whl
Size 27.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ad7d04a22c74dfe327c39a73dd8341250127086f8b305edc5d59362a939c7c72
BLAKE2b-256 checksum
How to use checksums
5547dad188ad7bc71547560dc0826071bcfa2a972597bb06d099b868f32ae500
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 11, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.2 This release

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page