Business automation — Python, Rust, TypeScript. Bulgaria, working with clients across the EU, UK and US.
I build the unglamorous tools businesses run every day: data reconciliation, catalogue and deliverability audits, webhook-to-CRM integrations, and the monitoring that tells you an automation stopped working before a customer does.
Everything below is open source and runs on a clean machine with one command. Each repository has its own tests in CI, documents what it deliberately does not do, and where a README claims a number, the script that produced it is in the repository.
How many sites publish an llms.txt has been counted several times this year. This is the next question: of the 123 files found among the 1,500 most visited sites, 13 belong to sites whose robots.txt bans, by name, a crawler that would read them — and 13.5% of the files point at least one link at a page that is gone. A map with dead streets is worse than no map.
A security.txt tells a researcher where to report a hole in your site, and RFC 9116 makes its Expires field mandatory — an expired file is, in the words of the specification, as good as no file. Of the 158 such files among the 500 most visited sites, 92 carry no Expires at all and 6 have expired: 62% are not valid. Nothing warns when that happens.
Google and Shopify published the Universal Commerce Protocol so an agent can read a shop and buy from it. Nobody had published a count of how many shops actually speak it, so I measured it: 98.9% publish a valid profile, every one of them on the current release, none with a blocker. The only thing they differ on is whether an agent may sign the shopper into their own account — and 47.5% do not allow it, which turns a returning customer into an anonymous visitor.
On 23 October 2026 a batch of OpenAI identifiers retires in one day — twenty-nine of them in this tool’s table, counting every section of the deprecation page. Four more went twenty-five days earlier, on 28 September. GitHub code search puts 96,640 files on gpt-4-0613 alone. Every one is a hardcoded string that starts returning an error on a published date, and nothing warns first. This answers the question that comes before “how do I migrate”: does it affect me, and how long have I got. One run, no key, exit 1 for CI — and it will not flag gpt-4o as dead just because gpt-4 is.
A workflow keeps running right up to the day it does not: the runner image is retired, the language version stops getting security fixes, the action's major is switched off — each on a date published months earlier, none of which warns you first. GitHub code search counts 1,335,296 files naming actions/checkout@v3. Dependabot moves versions; it does not say what stops working on the eleventh of a month. Run against my own repositories it found Python 3.10 in seventeen of them, forty days out — including its own CI.
An llms.txt is a file a site publishes so that an assistant will read it. This measures how often a model used as an agent obeys an instruction planted in that file instead of your request. Nineteen local models, temperature 0, scored by a canary substring so no judge model is involved — and a control case that voids a model's numbers if it emits the canary unprompted. The data-exfiltration framing beats twelve of the thirteen hijackable models every single time; parameter count predicts nothing; a published mitigation helps ten models and makes two measurably worse.
When a provider swaps the embedding model under a live vector index, nothing raises an error — the nearest neighbours are simply wrong from then on, and the blame lands on the language model rather than the retrieval step. This pins a fingerprint and reports two numbers: vector drift catches any change including a harmless renormalisation, while drift in the geometry between probes only moves when retrieval will actually return different rows.
Every company does the same loop: open a PDF or an email, find a handful of fields, type them into something structured. This does that loop once, repeatably — you describe the fields in YAML and get exactly those keys back, across Anthropic, OpenAI or a local Ollama model with one flag. The part that matters is the failure: a reply that does not fit the schema is caught, reported and retried against the declared shape rather than written downstream. A built-in demo provider runs the whole pipeline with no API key at all.
An assistant's crawler fetches HTML and stops: no JavaScript, no inference. A site built for Google can be invisible to it while every SEO tool calls the site fine — a robots.txt copied from a template blocks GPTBot by name, or the page ships forty words of markup behind nine hundred kilobytes of script. Reads robots.txt per named crawler, follows the links an llms.txt promises, and judges each page on what is in the HTML. 31 of 31 offline self-checks pass.
A lender exports a loan tape and sends it again — tomorrow, twice in one day, sometimes with one field corrected and the rows in a different order. Any system fed by someone else's export has this problem, and getting it wrong means a ledger nobody can reconcile. Three levels of repeat detection, cheapest first; a corrected field writes a correction event and one history row per change instead of a silent no-op; rows that do not parse go to quarantine. 28 self-checks on the import and 28 graded cases on the classifier, both green.
Diffs two CSV exports by key and reports what actually changed. Two million rows in 2.8 seconds, a per-column breakdown with counted transitions, and number/null normalisation — so a migration report reads as 9 809 real changes instead of 995 000 rows of timestamp noise.
Offline check of what leaves with a file before you send it: tracked changes with the deleted text still readable, review comments, hidden worksheets, PDF attachments, GPS coordinates in photos, validated card and IBAN numbers.
Run an ordinary link checker against an ordinary site and the report fills with 403s from Cloudflare and 999s from LinkedIn — none of them broken links, and after the second false alarm nobody opens the report again. This separates a link a visitor would find dead from a link a bot was refused, and only the first sets a non-zero exit code. 220 links on rust-lang.org in 10.5s; 28 tests, none touching the internet.
Perceptual-hash duplicate finder. Complete-linkage grouping, so unrelated pictures never chain together through a path of small steps — and it writes a delete script for you to read instead of deleting anything itself.
Crawls a site and reports the links that are genuinely broken, keeping bot protection and slow third parties out of the "broken" column — which is why the report still gets read after the second run.
Record the webhooks a provider sends you once, then replay them at your own code as often as you need — re-signed for Meta, GitHub, Shopify or Stripe, so the receiver runs its real verification instead of having it switched off for the debugging session.
A site publishes a dozen small files at its root — security.txt, robots.txt, llms.txt, assetlinks.json, mta-sts.txt — each checked by a different tool or by nobody. This reads all of them in one run and says which are missing, malformed, or quietly expired. RFC 9116 gives security.txt an Expires date; past it, the file is no longer valid, and almost nobody notices.
Google and Shopify's Universal Commerce Protocol lets AI agents shop: every business publishes a profile at /.well-known/ucp and agents read it to decide what they can do. The failure mode is silence — offer checkout without declaring a searchable catalogue and no agent ever surfaces your products. This reads the profile the way an agent does.
Since 11 September 2026 a manufacturer placing a product on the EU market has 24 hours to notify ENISA of an actively exploited vulnerability in it. Finding vulnerabilities is not the hard part — any scanner hands you a hundred. This says which of them starts the statutory clock, and drafts the Article 14 notification for the ones that do.
Records the shape of a JSON API and reports when it changes: removed fields, changed types, values that can now be null. Exits non-zero in CI, so a partner's silent rename stops being something you find out about two weeks later.
Matches a bank statement against open invoices in five passes — references, part payments, batch transfers, instalments — and reports only what does not add up. Standard library only.
A webhook arrives, Python cleans, validates, scores and de-duplicates the payload, then it is written to a sheet and announced in Slack; a second module reads a PDF and decides whether it can be auto-approved or has to go to a person. n8n owns the plumbing, Python owns the logic that would be painful inside a no-code node — and testable.
The blocker in “connect AI to our data” is not the wiring, it is what stops the model writing, dropping, or reading the salaries table. Answered in configuration rather than in trust: read-only connection, table allowlist, PII masking, row cap, query timeout, and an audit log of every question asked.
WhatsApp Business Cloud webhooks into a CRM: signature verification, instant acknowledgement plus queue, de-duplication, conversations logged against both the contact and the company, the 24-hour window, token refresh.
A key in a commit is public the moment it is pushed and stays in the history after it is deleted, so the only cheap moment to catch it is before the push. One Python file, no dependencies, nine rules for the shapes that actually leak, findings redacted to first and last four characters, exit 1 so a pre-commit hook or a CI step stops. This is the gate I run over my own repositories before every push.
Finds why a domain's mail lands in spam: SPF lookup limit, duplicated records, revoked DKIM keys, DMARC stuck at p=none, missing MTA-STS. DNS only — no access to the mailbox needed, batch mode for client lists.
Merchants export a whole catalogue to spreadsheets just to find what is broken: products with no image, descriptions of nine words, sold-out pages still collecting clicks. This reads any Shopify store from the outside — no API key, no app install — and returns a prioritised report and a CSV fix-list in under a minute.
Competitor prices and restocks get tracked by opening tabs and typing numbers into a spreadsheet, so a change is noticed whenever someone next looks. A CSS selector per product instead, watched on a schedule, with every observation kept so a claim about last week can be checked rather than remembered.
A follower count on a profile card has to be true. The awkward parts are not the API call: dozens of creators share one rate limit, tokens expire every sixty days, and a stale number presented as live is worse than none. Read-only scopes enforced in code, and every number carries when it was fetched.
Most scraping scripts work on the author's machine and fail on the fifth page. This is the same job done the way a paid one has to be: request pacing, retries with exponential backoff, user-agent rotation, normalisation, deduplication — and a row that does not parse is reported rather than quietly dropped.
An n8n automation fails loudly, or by quietly not running any more. The second is worse and nothing in a default install mentions it. Three importable workflows: normalise a failure and route it to an alert, catch the workflow that stopped, and prove the alert route works before it is needed.
Seven clean exports for the jobs that come up every time: lead capture, an RSS digest, form submissions into a CRM without duplicates, and an authenticated webhook that answers with a status code a sender actually reads. The value is in the parts people get wrong the first time.
Answers a web enquiry in under a second: scores it, writes a personal reply, offers real call slots, escalates the hot ones and files the spam. Rules live in YAML, no API keys.
curl -sSL https://github.com/dkautomation23/csvdiff/releases/latest/download/csvdiff-v1.0.0-x86_64-unknown-linux-gnu.tar.gz | tar xz ./csvdiff-v1.0.0-x86_64-unknown-linux-gnu/csvdiff before.csv after.csv --key customer_id --ignore updated_at
Every release carries a binary for Linux, macOS (Apple silicon) and Windows, each built and tested on the platform it targets rather than cross-compiled.
A workflow that stopped running, an API that changed shape, a feed half-empty for a fortnight. Monitoring that names the failure, not a dashboard nobody opens.
Webhooks into a CRM, a bank statement against invoices, a catalogue against a supplier feed. Signature verification, de-duplication, retries and the 3am cases.
Migration checks, reconciliation, audits of a catalogue, a domain's mail or a site's crawlability — with the numbers reproducible rather than asserted.
An automation someone else built and left. I will read it and tell you honestly whether it is worth repairing or worth replacing.
Tell me what breaks today and what it costs you: hello@dkautomation.dev. You get back an approach and a number, or an honest “this is not my job” and who to ask instead.