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...

PostgreSQL 18 UUIDv7: A Better Default for New IDs?

Image
PostgreSQL 18 UUIDv7: A Better Default for New IDs? UUIDs are useful when identifiers need to be created independently, but UUIDv4 values are random. When a UUID is the primary key of a B-tree index, inserting random values can send writes to many different parts of the index. PostgreSQL 18 adds a built-in alternative: uuidv7() , which generates a time-ordered UUID. That makes UUIDv7 worth considering for new tables—but it is not a universal performance fix, a precise event clock, or a secret identifier. What UUIDv7 changes A UUID is still 128 bits. In UUIDv7, the most significant 48 bits encode a Unix timestamp in milliseconds. The remaining bits contain random data and may include implementation-specific monotonicity information. This puts values generated around the same time near one another when compared in their standard byte order. The RFC describes UUIDv7 as time-ordered and notes that random UUID versions can have poor database-index locality. PostgreSQL 18 provides uuidv7...

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...