Posts

A New Collection of Thoughtful Learning Apps — Now Available on iOS & Android

Image
I’m excited to share a set of mobile apps I’ve recently completed and published on both the Google Play Store and the Apple App Store. These apps are designed with a simple goal in mind: to make meaningful, structured content more accessible, whether you’re studying theology or improving your English vocabulary. 📱 Now Available on Both Platforms All apps are live and available for download: Google Play Developer Page: https://play.google.com/store/apps/dev?id=5835943159853189043 Apple App Store Developer Page: https://apps.apple.com/ca/developer/q-z-l-corp/id1888794100 📖 Theology & Confession Study Apps For those interested in Reformed theology and classical Christian teachings, I’ve developed a series of apps that present foundational texts in a clean, focused reading format: The Belgic Confession Canons of Dort Heidelberg Catechism Westminster Shorter Catechism Each app is designed to provide a distraction-free experience, making it easier to read, reflect, and revisit these im...

Your Spring Boot Transaction Doesn't Make Event Publishing Reliable

Image
Your Spring Boot Transaction Doesn't Make Event Publishing Reliable A request marks an order as paid and then publishes an OrderPaid event. Both calls may succeed in a normal test. But the database and a message broker are separate systems: the database can commit while the broker is unavailable, or the broker can accept a message just before the database transaction rolls back. A Spring @Transactional method does not, by itself, make those two systems one atomic transaction. This gap is a common source of events that are missing, duplicated, or inconsistent with the business record. A transactional outbox narrows the gap by making the business change and the intent to publish an event part of the same database transaction. Write the event intent with the business change Instead of calling the broker inside the request's database transaction, insert a durable outbox row alongside the order update: BEGIN; UPDATE orders SET status = 'PAID' WHERE id = :order_id; INSE...

Rest Is Not a Reward for Finishing Everything

Image
Rest Is Not a Reward for Finishing Everything There is always one more thing to do. An email waits unanswered, a room still needs attention, or a task has slipped into tomorrow. If rest begins only when every responsibility is finished, it may never begin at all. Scripture offers a different starting point: rest is not a prize we earn by completing enough work. It is a gift and a command that remind us who made us, who provides for us, and where our hope finally rests. Rest begins with God's completed work Genesis describes God resting on the seventh day after completing his work of creation. This is not a picture of God recovering from exhaustion. His work is complete; he blesses the day and sets it apart. The rhythm of work and rest is built into the story of creation before it is given to Israel as a command. That pattern tells us something humbling: we are creatures, not the keepers of the universe. Our work matters, but it is not what holds all things together. Stopping ca...

Spring Boot Observability: Propagating Trace Context Across Async Threads

Image
Spring Boot @Async: Fixing Distributed Tracing Context Propagation Across Threads Have you ever noticed that distributed traces suddenly disappear when your Spring Boot application executes an asynchronous task? A request enters your application, calls a service, and then schedules background work using @Async . The initial request appears correctly in your tracing dashboard, but the background execution is missing—or worse, it appears as an entirely unrelated trace. The problem may not be your tracing backend. It may be that the request's observation context was not propagated to the worker thread. Distributed tracing relies on context: identifiers and other state that connect operations across services and execution boundaries. OpenTelemetry provides mechanisms to propagate this context between services, usually without requiring application code to copy identifiers manually. However, there is another boundary within the same application that is easy to overlook: switching thre...

Publish npm Packages from GitHub Actions Without a Long-Lived Token

Image
Publish npm Packages from GitHub Actions Without NPM_TOKEN: Using Trusted Publishing with OIDC Publishing an npm package through GitHub Actions traditionally requires a secret such as NPM_TOKEN . While this approach works, it also means storing a reusable publishing credential in your repository's automation configuration. There is a better alternative: npm Trusted Publishing . With Trusted Publishing, GitHub Actions authenticates to npm using OpenID Connect (OIDC). Instead of storing a long-lived npm publish token, GitHub identifies the workflow, and npm verifies whether that workflow is authorized to publish the package. The practical benefit is reduced credential exposure. Your release workflow no longer needs a long-lived npm publishing secret. This does not guarantee that your package or workflow is secure, but it removes one persistent credential from the release process. How npm Trusted Publishing Works The process relies on a trust relationship between npm and GitHub Acti...

Building an Always-Online Personal AI Agent on an Oracle Cloud VPS with Hermes, GitHub Copilot, and Telegram

Image
  Building an Always-Online Personal AI Agent on an Oracle Cloud VPS with Hermes, GitHub Copilot, and Telegram What if you could have your own AI assistant running 24/7 on a server, accessible from anywhere, without keeping your laptop running? Imagine sending a message to a Telegram bot while commuting, asking it to research a technical topic, write a blog post, investigate an issue, or execute a task on your server. A few moments later, your personal AI agent responds. It can also run scheduled tasks, work independently, and continue operating even when your computer is turned off. That was the idea behind my latest experiment: building an always-online personal AI agent using an Oracle Cloud VPS, Hermes Agent, GitHub Copilot, and Telegram. This article documents the complete setup journey, including the installation, integration, configuration, and some problems encountered along the way. 1. The Architecture The goal was to build a personal AI assistant with four main components...

How to SSH into an Oracle Cloud VPS from Android Using Termux and Cloudflare Tunnel

Image
  How to SSH into an Oracle Cloud VPS from Android Using Termux and Cloudflare Tunnel You don't need a public IP address to SSH into your Oracle Cloud VPS from your Android phone. With Termux, OpenSSH, Cloudflare Tunnel, and Cloudflare Access , you can securely connect to your Oracle Cloud VPS directly from your Android device. In this tutorial, I'll show you how to configure everything step by step. 1. Architecture Here is how the connection works: Android Phone | v Termux + OpenSSH | v cloudflared (ProxyCommand) | v Cloudflare Access | v Cloudflare Tunnel | v Oracle Cloud VPS | v localhost:22 (SSH) The VPS does not need a public IP address or an exposed SSH port. Cloudflare Access handles authentication, while SSH public-key authentication verifies the user on the VPS. 2. Prerequisites Before starting, make sure you have: An Oracle Cloud VPS running Linux. A Cloudflare Tunnel already connected to your VPS. A Cloudfl...