Python & web developer in Miramichi, New Brunswick, Canada. I build websites, Stripe payment integrations and Python automations for small businesses, and I run my own product, DeMark Studio.
- 🌐 Portfolio: marvin.demarkstudio.ca (en español)
- 💼 Hire me: Upwork · Fiverr · LinkedIn
- 📊 Latest study: I checked 2,417 small-business websites in Atlantic Canada; one in seven didn't work at all
- ✅ Is your site OK? Free instant grade: demarkstudio.ca/en/report-card
- 🗣️ English & Spanish
83 small, tested tools. Each one was run against my own live sites before publishing; the README shows that real output.
SEO and crawling
| Project | What it does |
|---|---|
| site-seo-crawler | Crawls your own site and reports technical SEO problems: broken links, redirects, titles, canonicals, hreflang, sitemap gaps, JSON-LD. 51 tests. |
| sitemap-diff | Audit XML sitemaps (status, noindex, canonical, robots.txt conflicts, orphan pages) and diff them across a site migration |
| hreflang-check | Validate hreflang for multilingual sites: codes, return links, x-default, canonical conflicts, html lang |
| indexability-check | Is each page indexable, and should it be? Robots, noindex, canonicals, sitemaps and their conflicts |
| redirect-checker | Verify redirect maps and canonical hosts: chains, loops, 302 vs 301, https downgrades, www and trailing slashes |
| robots9309 | robots.txt matching per RFC 9309 (most specific rule wins, * and $ wildcards); fixes urllib.robotparser's first-match bug. 15 tests. |
| local-business-schema | Generate LocalBusiness JSON-LD and check structured data and NAP consistency across a site's pages |
| og-card-check | See how your links look when shared: Open Graph, X cards and image checks with rendered card previews |
| og-image-gen | Generate 1200x630 social share images from templates with text fitting and a WCAG contrast check |
| untranslated-check | Find untranslated or mixed-language text on multilingual sites, and links that switch language |
| readability-check | Readability of web copy in English, French and Spanish with per-language formulas and the hardest sentences |
| website-health-audit | Ranks small-business websites from worst to best: expired or parked domains, spam takeovers, no HTTPS, not mobile-friendly, no online booking. Standard library only. |
| soft-404-check | Check how a site handles missing pages: real 404s vs soft 404s and redirects to home |
| sitemap-gen | Generate XML sitemaps from a build folder (lastmod from git) or a polite crawl, with hreflang |
| schema-lint | Validate structured data against the real schema.org vocabulary and Google rich-result requirements |
| landing-page-check | Check an ads landing page like paid traffic sees it: tracking kept, message match, CTA, speed, consent |
| i18n-diff | Compare translation files: missing keys, untranslated text, placeholder mismatches, French typography |
| redirect-map-gen | Build a redirect map for a migration and generate Caddy, nginx, Apache and Netlify rules |
| product-feed-gen | Google Merchant feed, Meta catalog and Product JSON-LD from a spreadsheet, validated, bilingual EN/FR |
| anchor-check | Find broken #fragment links, skip links that go nowhere and ARIA references to missing ids |
Performance
| Project | What it does |
|---|---|
| web-vitals-lite | Lab Core Web Vitals without Lighthouse: LCP, CLS, TBT, FCP, TTFB on throttled mobile and desktop, with CI budgets |
| image-audit | Find image bytes to save: oversized images, WebP/AVIF savings, lazy loading and LCP issues per device |
| unused-code-audit | Measure unused JavaScript and CSS per page with Chromium coverage, render-blocking files and CI budgets |
| cache-header-audit | Audit HTTP caching and compression of a page and its assets, with exact Caddy, nginx and Apache fixes |
| visual-diff | Visual regression for websites: frozen-animation screenshots, masks, pixel diffs and an HTML report |
| web-font-audit | Audit web fonts: formats, font-display, preloads, unused @font-face and real subsetting savings |
| http-protocol-check | Check HTTP/2, HTTP/3, accepted TLS versions, certificate chain, HSTS, compression and IPv6 |
| image-optimizer | Batch-optimize images: responsive widths, WebP/AVIF at the lowest quality above an SSIM threshold, srcset snippets |
| font-subsetter | Subset web fonts to the characters pages use, convert to WOFF2 and write @font-face with unicode-range |
| critical-css | Extract and inline above-the-fold CSS, load the rest without blocking, verified by pixel comparison |
| asset-fingerprint | Cache-busting for static sites: hash assets and rewrite references in HTML, CSS and manifests |
Security and privacy
| Project | What it does |
|---|---|
| security-headers-audit | Grade a site's HTTP security A-F (HSTS, CSP, framing, cookies, TLS) with the exact header to fix each finding |
| csp-builder | Build a tight Content-Security-Policy from what pages really load, with hashes and a diff against your current policy |
| script-inventory | Inventory every script a page runs: SRI verified, library versions with known advisories, mixed content, CSP hints |
| consent-tracker-scan | Shows the cookies, storage and third-party trackers a website loads before the visitor consents (Quebec Law 25 / PIPEDA context). 110 tests. |
| consent-banner-lite | Dependency-free cookie consent banner (4.8 KB gzipped): equal Reject button, script blocking, Consent Mode v2, EN/FR/ES |
| cookie-policy-gen | Generate an English/French cookie disclosure page from a real tracker scan, with a diff to keep it true |
| security-txt | Check and generate RFC 9116 security.txt files, with expiry watch and CI exit codes |
| dns-health | DNS health: nameservers, SOA, lame delegation, DNSSEC, CAA vs the real certificate, dangling CNAMEs |
| email-dns-check | Check SPF, DKIM, DMARC, MX, MTA-STS and TLS-RPT for a domain and get the exact DNS record to publish |
| domain-ssl-watch | Watch TLS certificate expiry, domain registration (RDAP), DNS and HTTP for your domains, with cron exit codes and optional email/Telegram alerts |
| form-spam-guard | Stop form spam without CAPTCHAs: honeypot, signed single-use time tokens, script and link filters, rate limiting |
| contact-form-backend | Self-hosted contact form backend with CAPTCHA-free spam protection and SMTP delivery |
| secret-scan-web | Find secrets leaked into what a site serves: JS bundles, source maps, exposed .env or .git files |
| cors-check | Test CORS with real requests: reflected origins, null, wildcard with credentials, look-alike origins |
| log-redact | Redact personal data and secrets from logs: emails, phones, cards, SIN, IPs, tokens, with pseudonyms |
| compose-audit | Security audit of docker-compose files: privileged, docker.sock, open ports, secrets, root, limits |
| homoglyph-check | Detect deceptive text: mixed scripts, look-alike characters, invisible controls, IDN look-alike domains |
Accessibility and front-end quality
| Project | What it does |
|---|---|
| a11y-audit | Accessibility audit: axe-core plus keyboard focus, 200% zoom and reduced-motion checks, grouped by WCAG criterion |
| contrast-palette | WCAG and APCA contrast, nearest passing colour in OKLCH, 50-950 palettes and a check of every text colour on a page |
| form-audit | Audit web forms read-only: labels, autocomplete, mobile input types, error wiring, touch targets |
| html-lint | Lint served HTML: duplicate ids, bad nesting, missing labels and alt, heading order, SARIF output for GitHub |
| webmanifest-check | Check the web app manifest and real icon sizes, and generate the missing icons |
| a11y-statement-gen | Honest English/French accessibility statements from an a11y-audit report, and progress between audits |
| pdf-a11y-check | Check PDFs for accessibility: tagging, title, language, real text, fonts, alt text, metadata |
| focus-order-map | Make keyboard order visible: numbered tab stops on a screenshot, off-screen focus and traps flagged |
| Project | What it does |
|---|---|
| html-email-lint | Lint HTML emails before sending: Gmail clipping, CSS support per client, links, dark mode, contrast, screenshots |
| email-css-inliner | Dependency-free CSS inliner for HTML emails: real cascade, keeps @media and Outlook MSO comments |
| email-preview | Preview HTML emails in light, three dark-mode styles and images-off, with contrast flags |
APIs, payments and business
| Project | What it does |
|---|---|
| site-api-mapper | Maps the HTTP/JSON API a website uses into an OpenAPI 3 spec + Markdown, from a HAR file, a passive headless capture or its JavaScript. Redacts secrets by default. 133 tests. |
| openapi-lint | Lint OpenAPI documents: operationIds, error responses, security, examples, pagination, rate-limit headers |
| webhook-sender | Reliable outgoing webhooks: HMAC signatures, retries with backoff, circuit breaker, outbox and replay |
| api-diff | Compares two OpenAPI specs and flags breaking changes, with CI exit codes. 50 tests. |
| openapi-client-gen | Generates a small, typed Python client (httpx + dataclasses) from an OpenAPI spec. 106 tests. |
| webhook-inspector | Self-hosted webhook receiver and debugger: verifies Stripe, GitHub and Shopify signatures, stores, replays and exports requests. 60 tests. |
| stripe-fastapi-webhooks | Stripe Checkout with a verified, idempotent webhook in FastAPI. Signature check without the SDK, replay protection, a late "failed" never overwrites "paid". 16 tests. |
| stripe-subscriptions-starter | FastAPI + Stripe subscriptions: Checkout, Customer Portal, verified idempotent webhooks that survive out-of-order delivery. 33 tests. |
| canada-sales-tax | Canadian GST/HST/PST/QST by province and date, exact cents, tax-included prices that add up, EN/FR invoice lines. No dependencies. |
| invoice-pdf-canada | One-page Canadian PDF invoices from JSON, with GST/HST/PST/QST by province and date, in English or French. 37 tests. |
| overdue-invoice-reminders | Finds overdue invoices in a CSV export and sends tiered email reminders. Dry-run by default. |
| stripe-tax-report-ca | GST/HST/PST/QST report from Stripe CSV exports: collected vs expected per province and period |
| ics-lint | Validate and generate iCalendar files: folding, UIDs, time zones, iTIP REQUEST/CANCEL rules, client quirks |
| sms-segments | SMS length and cost: GSM-7 vs UCS-2, segments, the characters that double the cost, safe replacements |
| email-list-hygiene | Clean a mailing list without sending anything: duplicates, typos, disposable domains, MX, consent |
| opening-hours | Parse English/French opening hours into schema.org JSON-LD, OSM syntax and a DST-safe 'open now' widget |
| canada-address | Normalize and validate Canadian addresses offline: postal code vs province, EN/FR street types, units |
| qr-vcard | vCards and QR codes for small businesses (URL with UTM, Wi-Fi, SMS), every code verified by decoding |
| ca-business-number | Validate Canadian business numbers offline: BN9 check digit, GST/HST RT accounts, QST, invoice rules |
Monitoring and CI
| Project | What it does |
|---|---|
| website-report-card | One client-friendly report from eight website checks: grade, traffic lights, top fixes in plain language, PDF |
| status-page-gen | Static status page from cron checks: HTTP, TCP, TLS, 90-day uptime bars, incidents in Markdown, Atom feed |
| page-change-watch | Watch pages for meaningful changes with noise filters, snapshots, readable diffs and cron exit codes |
| website-ci-checks | GitHub Action that runs my website checks in CI with a job summary and SARIF upload |
| sqlite-backup-check | Safe SQLite backups: online backup while writing, rotation, checksums, encryption and a restore test |
Python · FastAPI · Stripe (Checkout, Billing, webhooks) · SQLite · Docker · Caddy · Cloudflare · HTML/CSS/JavaScript · Local SEO