mz.status

Projects

3 in progress

Not a skills list — things I've actually worked on, most of them still with a lot left to learn.

aioperational

LifeOS — a self-hosted AI assistant

A personal AI assistant I'm building for myself: a FastAPI backend behind a Telegram bot and a small React web app, routing between a local Ollama model and OpenRouter depending on the task.

  • Handles goals, calendar, and budget tracking through conversation, with voice input/output and Google Calendar integration.
  • Self-hosted on a k3s cluster on Oracle Cloud, Terraform-provisioned with CI/CD deploying on every push to main — started as a WSL2 box at home, now kept running as a cold-standby rollback instead of being torn down.
  • Custom domain (lifeos.michalzawadzki.dev) backed by a Terraform-managed Cloudflare DNS record — imported from a hand-created one into IaC rather than left undocumented, DNS-only/unproxied so cert-manager's HTTP-01 challenge and the app's SSE/voice streaming don't pick up an extra proxying layer.
  • Routes per-request, not per-conversation: a local Ollama model handles transactional tool-calling (expenses, calendar, health logs), and a purpose-built redaction gateway strips household names before anything genuinely open-ended gets handed off to a cloud model.
  • 90 pytest tests and GitHub Actions CI cover the tool-dispatch table, the finance/calendar/health clients (mocked against real API responses), and the full session API — caught real bugs before they shipped, including a units mismatch that inflated a day's tracked calories by 7x.
  • Health data syncs itself: an iPhone automation (Health Auto Export) pushes a JSON payload to a bearer-token-gated endpoint whenever new data lands, parsed into daily metrics and workouts so the model can pull it into a morning/evening brief or answer "how was my week" on demand — no manual export step.
  • Security is scoped deliberately, not bolted on: Google OAuth behind an email allow-list with a signed session cookie, a constant-time bearer-token check gating the one webhook exposed on the public tunnel (the health sync endpoint above), a redaction gateway with its own test suite, and the DB admin UI (Adminer) bound to localhost only, never routed through the tunnel.
#ai#llm#self-hosted#automation
aioperational

AI-assisted booking system for a small service business

A client management and scheduling tool I built for someone close to me who runs a one-person, appointment-based business — natural-language input instead of forms, on top of a self-hosted stack.

  • What's live today didn't start on Vercel — the first version was a self-hosted FastAPI + Postgres + Redis stack in Docker Compose, tunneled out via ngrok from a home computer, so the whole booking system went down whenever that machine was off or the power blipped.
  • Rewrote it as a single Next.js app — API routes and frontend together — deployed on Vercel with Supabase as the managed database, so uptime no longer depends on a home PC; the local Ollama chat model is now the only piece still tied to that machine, reached through its own secured tunnel, same pattern as LifeOS.
  • That rewrite meant redoing auth too: swapped a hand-rolled OAuth flow (raw httpx calls plus itsdangerous-signed cookies) for Auth.js — kept the same email-allowlist trust model, even the same environment variable name for continuity, with far less auth code left to maintain.
  • A GitHub Actions pipeline runs the backend test suite against a disposable Postgres database on every push, since a bug here affects someone else's actual income.
  • Two-way SMS reminders through a self-hosted gateway running on an actual Android phone's SIM card, instead of a paid per-message API.
  • An AI chat assistant running on a local model, so client names and notes never leave the machine — the harder part was getting tool-calling reliable enough to trust with real bookings.
  • Beyond bookings — client documents with an on-canvas signature saved as a generated PDF, and a business finance view (revenue, costs, per-period totals) kept entirely separate from the owner's personal budget.
  • Prepaid packages and vouchers that draw down over visits, plus a before/after photo log per client — both stored in private Supabase buckets, never public.
  • A product resale ledger — catalog with stock levels and a per-sale record that snapshots cost at the time of sale, so margin stays accurate even after supplier prices change later.
  • A second Supabase + Vercel environment, seeded with realistic-but-fake Polish client data, so this write-up could ship with real screenshots without exposing an actual client's information — its schema migrations apply automatically in CI on every merge, deliberately kept separate from production's manual, reviewed rollout.
#ai#automation#self-hosted
devopsoperational

This site's own publish pipeline

Merge a Markdown post to main and it deploys, then publishes itself to LinkedIn with a link back — no manual copy-paste step.

  • GitHub Actions handles the full flow: build, deploy, wait for the live URL, then publish to LinkedIn via the official REST API.
  • The deploy check used to just poll for a 200 — which would pass even against a stale cached page. It now also confirms the actual post title is present in the response body before publishing goes ahead, with unit tests covering that logic so it can't silently regress.
  • Everything is versioned and reviewed through a normal pull request, same as any other change to the site — CI runs typecheck, the full unit test suite, and a production build on every push.
  • A weekly scheduled job pulls from a handful of RSS feeds, drafts a bilingual post with an LLM against a fixed voice/style prompt, and opens a PR — nothing publishes without a human reading it first.
  • A GitHub OAuth-gated admin panel gives a manual path alongside the automated one — writing and editing posts directly, and moderating comments — without needing a PR for every small fix.
  • The site itself is now properly discoverable: JSON-LD structured data, dynamically generated OG images per post, sitemap/robots.txt, and canonical/hreflang tags across both locales; a public RSS feed and Vercel Web Analytics track who's actually reading.
  • Reader-facing polish followed the same pipeline discipline — a light/dark theme (recolored via CSS variables, not per-component), a reading-progress bar, tag-scored related posts, and a full tags index, all shipped through the same PR + CI review as everything else.
  • Sentry now watches client, server, and edge runtimes for unhandled errors — error capture only, tracing and session replay deliberately left off since neither earns its bundle-size cost at this site's traffic. No-ops safely if the DSN isn't set, so it never breaks a build.
#devops#automation#ci-cd#linkedin