Thursday, January 29, 2026

A Return After 13 Years: From Machine Learning Clusters to AI Conversations

Thirteen years is a long time in technology. When I last hit "publish" here in 2013, Bitcoin was a novel curiosity and building a 24-core cluster into an IKEA Helmer cabinet was a fun weekend project. The world has changed, and so have my projects—but my fascination with making powerful technology accessible and practical has not.

Back then, my friend Alex Nugent and I were obsessed with raw computing power, as seen in our 2008 post about that 24-core cluster. Today, Alex and I are building something different: AI Receptionist, a practical tool born from the real frustration of modern business communication. (More on that shortly).

Some things do remain constant. I've always believed in building lasting tools. Case in point: our open-source Java library, XChange, is still actively maintained on GitHub after all these years. It's a testament to building with care and for the community.

The AI Tool Landscape: Power Demands Responsibility

The tech world today is buzzing with agentic AI—tools that don't just answer questions but take actions. One project, Moltbot (formerly Clawdbot), recently captured this hype, exploding on GitHub. Its promise is compelling: a personal, self-hosted AI assistant.

However, its rapid rise was marred by serious security oversights, from exposed API keys to critical vulnerabilities in its architecture. This pattern is unsettlingly familiar. It mirrors the core security dilemmas we've written about before, like the risks of building production systems on platforms such as n8n (which we detailed here).

These incidents underscore a critical rule: any tool granted high-level access to your digital life must be treated with corresponding seriousness. Whether it's a local LLM agent, an MCP server from an open-source project, or an automation tool from Zapier, you must be vigilant about the permissions you grant. Security isn't a feature; it's the foundation. The power to act is also the power to cause harm if that access is not meticulously controlled and isolated.

Many guides assume you're running it on a dedicated local machine. But you don't need a Mac Mini (a nod to our ancient 500GB MacBook upgrade guide for those who remember!). A well-configured VPS can work, provided you prioritize isolation, strict firewalls, and treat its access tokens with the gravity of root passwords.

Our New Chapter: AI Receptionist

So, what have Alex and I been building with all these security and reliability principles in mind?

AI-Receptionist.com solves a simple, painful problem: small businesses are drowning in robocalls and spam, missing real customers in the noise. We've combined decades of experience in AI, distributed systems, and yes, even low-level hardware (hello, Helmer cluster) to build an intelligent, 24/7 phone agent.

It's more than an answering machine. It understands context, filters spam intelligently, and ensures no genuine call is missed—all at a fraction of the cost of a human receptionist. Crucially, it's built as a secure, fault-tolerant service from the ground up. It's the applied, reliable implementation of the AI principles that much of the current hype is about, designed to operate in the real world where security and privacy are non-negotiable.

A Question for the Past

Writing this feels like sending a message in a bottle to a different internet era. To those who commented on those old posts about Linux clusters and Bitcoin—are any of you still out there? If you are, drop a note. I'm curious what you're building now and what you think of this wild AI landscape.

The goal of this blog was always "obscured clarity"—cutting through noise to the practical heart of technology. That mission continues. Let's see where the next conversation takes us.

Tim

Friday, August 2, 2013

How Bitcoin Works

I recently found one of the best technical explanations of Bitcoin and wanted to share...


Sunday, January 20, 2013

Unit Testing with HSQLDB

The latest release (2.0.0) of Yank - the Ultra-Light JDBC Persistance Layer for Java, finally contains unit-tested code. This blog is about how HSQLDB was used for performing in-memory unit tests using JUnit. Just like most things, once you know the few tricks, it's really easy.

Once nice feature of HSQLDB is that you can set up 100% in-memory tables, which makes unit testing a snap because you don't need to worry about having a database setup on the machine running the database. The following code snippets show how easy it was to setup a unit test for testing the core JDBC persistance layer code in Yank. While this is specific to Yank, this example should help you unit test any of your JDBC code using HSQLDB. After all, the main trick is to have your database properties setup correctly, as shown in HSQL_DB.properties below.

TestBooksTable.java

package com.xeiam.yank.unit;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;

import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

import com.xeiam.yank.DBConnectionManager;
import com.xeiam.yank.PropertiesUtils;
import com.xeiam.yank.demo.Book;
import com.xeiam.yank.demo.BooksDAO;

/**
 * @author timmolter
 */
public class TestBooksTable {

  @BeforeClass
  public static void setUpDB() {

    Properties dbProps = PropertiesUtils.getPropertiesFromClasspath("HSQL_DB.properties");
    Properties sqlProps = PropertiesUtils.getPropertiesFromClasspath("HSQL_SQL.properties");

    DBConnectionManager.INSTANCE.init(dbProps, sqlProps);
  }

  @AfterClass
  public static void tearDownDB() {

    DBConnectionManager.INSTANCE.release();
  }

  @Test
  public void testBooksTable() {

    BooksDAO.createBooksTable();

    Book book = new Book();
    book.setTitle("Cryptonomicon");
    book.setAuthor("Neal Stephenson");
    book.setPrice(23.99);
    int i = BooksDAO.insertBook(book);
    assertThat(i, equalTo(1));

    List<Book> books = new ArrayList<Book>();

    book = new Book();
    book.setTitle("Cryptonomicon");
    book.setAuthor("Neal Stephenson");
    book.setPrice(23.99);
    books.add(book);

    book = new Book();
    book.setTitle("Harry Potter");
    book.setAuthor("Joanne K. Rowling");
    book.setPrice(11.99);
    books.add(book);

    book = new Book();
    book.setTitle("Don Quijote");
    book.setAuthor("Cervantes");
    book.setPrice(21.99);
    books.add(book);

    int[] returnValue = BooksDAO.insertBatch(books);
    assertThat(returnValue.length, equalTo(3));

    List<Book> allBooks = BooksDAO.selectAllBooks();
    assertThat(allBooks.size(), equalTo(4));

    book = BooksDAO.selectBook("Cryptonomicon");
    assertThat(book.getPrice(), equalTo(23.99));

  }
}

HSQL_DB.properties

driverclassname=org.hsqldb.jdbcDriver

# 100% in memory DB
myconnectionpoolname.url=jdbc:hsqldb:mem:aname;shutdown=true
myconnectionpoolname.user=sa
myconnectionpoolname.password=
myconnectionpoolname.maxconn=10

HSQL_SQL.properties

BOOKS_CREATE_TABLE=CREATE TABLE Books (TITLE VARCHAR(42) NULL, AUTHOR VARCHAR(42) NULL, PRICE DECIMAL(10,2) NOT NULL)
BOOKS_SELECT_BY_TITLE=SELECT * FROM BOOKS WHERE TITLE = ?

Ultra-Light JDBC Persistance Layer

Yank is a very easy-to-use yet flexible Java persistence layer for JDBC-compatible databases build on top of org.apache.DBUtils. Yank wraps DBUtils, hiding the nitty-gritty Connection and ResultSet details behind a straight-forward proxy class: DBProxy. "Query" methods execute SELECT statements and return a List of POJOs. "Execute" methods execute INSERT, UPDATE, and DELETE (and more) statements.

Usage is very simple: define DB connectivity properties, create a DAO and POJO class, and execute queries.

Features

  • Depends on light-weight and robust DBUtils library
  • ~13KB Jar
  • Apache 2.0 license
  • Batch execute
  • Automatic POJO and POJO List querying
  • Works with any JDBC-compliant database
  • Write your own SQL statements
  • Optionally store SQL statements in a Properties file
  • Built-in Connection pool

What's Next?

Now go ahead and study some examples, download the thing and provide feedback.



Piece of Cake!!!

Thursday, January 17, 2013

XChange Release 1.3.0

Our Financial Exchange Library for Java, XChange, has seen a lot of active development since the previous release in October 2012. We went from 2 to 7 exchange implementations thanks mostly to the growing community starting to support the project more.

Here's a list of the supported exchanges. More detailed info can be found here, which includes planned future exchange implementations.
  • MtGox - polling and streaming market data, authenticated trading
  • Bitstamp - polling market data, authenticated trading
  • BTC-E - polling market data, authenticated trading
  • VirtEx - polling market data
  • CampBX - polling market data
  • BitcoinCharts - polling market data (Bitcoin Exchange Rates)
  • OpenExchangeRates - polling market data (Fiat Currency Exchange Rates)

Internally, we introduced a new and improved REST interface that sits between our xchange classes and the HttpTemplate class responsible for fetching JSON. It also gives XChange clients access to the raw unmarshalled JSON data if they want it, which was something XChange needed for a long time.

All exchange implementations have full-coverage unit tests.

We've been able to reduce the number of dependencies a lot. One of the main focuses of XChange is to be very lightweight. Most notably is the outdated org.json jar. We dug into the Socket.io code, and painstakingly swapped out the old code with our already-used Jackson JSON code. This is good news for apps like Bitcoinium and Multibit, which both use XChange, for keeping their executable footprint small.

Another major accomplishment with this release, is that the artifacts are now hosted on Maven Central: XChange artifacts on Maven Central

We're thinking about adding an arbitrage API within XChange next as the MtGox, BTC-E, and Bitstamp implementations all contain trading functionality.

Relevant Links

Detailed Exchange Support
Bug Reports and Feature Requests
XChange Home on xeiam.com
XChange artifacts on Maven Central
XChange project on Github

Monday, October 29, 2012

Unit Testing Runtime Exceptions with JUnit4 and Hamcrest

Junit and Hamcrest make unit testing with Java almost enjoyable. In this post, I show how to unit test Exceptions with JUnit4 and Hamcrest. In this over-simplified example, I'm testing a method that always throws an IllegalArgumentException. In real-life the IllegalArgumentException would only be thrown under special circumstances given certain arguments, and those are the ones you want to test with the unit test.

The basic idea is to surround the method you want to test with a try catch block and pass it an argument that will trigger the IllegalArgumentException to be thrown. The fail method call should never be reached, and if it does the unit test should fail. What should happen is that the IllegalArgumentException is caught, followed by a check for the correct message.

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.fail;

import org.junit.Test;

/**
 * The following example code demonstrates how to unit test Exceptions with JUnit and Hamcrest
 */
public class ExceptionUnitTesting {

  @Test
  public void test() {

    try {
      blah("asdf");
      fail("Expected exception");
    } catch (IllegalArgumentException e) {
      assertThat(e.getMessage(), is(equalTo("Argument foo is not valid!")));
    }
  }

  /**
   * @param foo
   * @throws IllegalArgumentException - in this test case always
   */
  private void blah(String foo) {

    throw new IllegalArgumentException("Argument foo is not valid!");
  }
}


Piece of Cake!!!

Monday, October 1, 2012

Import a Maven Git Project Into Eclipse

The Easy Way


Prerequisites
Install M2Eclipse

Step 1: Clone or create Git Repo. First, you need to clone a Git repo into an empty folder in your workspace. You need to use a Git client such as SourceTree or TortoiseGit for this. Create you workspace first if it isn't already, and clone the git repo directly into a new folder labeled with the project name.

Step 2: Import Into Eclipse. In the Package Explorer view, right-click and choose Import... In the wizard choose Maven -> "Existing Maven Projects" as the project type. Search for the project home directory and click though the wizard.

The Hard Way


Prerequisites
Install M2Eclipse
Install egit

Step 1: Right-click in the Package Explorer area and select Import..., select Maven -> Check out Maven Projects from SCM, and click Next.


Step 2: Choose "git" as the SCM connector next to "SCM URL:" and type in the Git repo URL. If there are no connectors to choose from you have to first install that Eclipse plugin. To do that, click on the "m2e Marketplace" link in the lower right hand corner of the dialog box.


Step 3: Check m2e-egit and click Finish.


Step 4: Select "git" as the SCM connector and type in the Git repo URL.


Step 5: After asking you for the passphrase for ssh access to the Git repo, it will download and import the Java project as a Maven project into your workspace.

Piece of Cake!!!