Extending the Single Binary: Framework Hooks, Embedded MCP, and Social Auth

How Moul evolved from a closed runtime into an embeddable Go framework with custom HTTP hooks, background worker extensions, native MCP server support, and OAuth2 authentication.

Phearak S. Tha
Phearak S. ThaFounder

A single-binary backend is convenient until you hit the boundary of what it supports out of the box. The moment you need a custom HTTP route, a bespoke background worker, or OAuth login, closed runtimes force you to choose between hacking the core source or managing external microservices.

When I first built Moul, I treated the binary as a finished appliance. You started the daemon, defined database collections over REST, and let the embedded SQLite engine handle sessions and queue workers. It was clean. It worked. But it felt isolated.

Real software doesn't stay inside pre-cut boxes. Eventually, you need an endpoint that talks to a payment gateway, a background worker that processes custom PDF reports, or a way for users to log in with GitHub. If an opinionated tool forces you to fork the repository just to add a 20-line HTTP handler, the single-binary philosophy has failed. It becomes another piece of rigid infrastructure.

Here is how the latest release opens up the architecture without adding external operational complexity.

Opening the Architecture (pkg/app)

To fix this, I refactored the server daemon (moul1) into an embeddable Go library. Instead of running a closed binary, you can now import moul-dev/pkg/app directly into your own Go main() function.

  • Custom Background Workers: You can register custom task functions using app.RegisterWorker("pdf:generate", handlePDF). The job executes inside the existing SQLite queue engine alongside standard system workers.
  • Custom HTTP Route Hooks: Using app.OnBeforeServe and app.Router(), you can attach standard Echo routes, custom middleware, and raw web handlers directly to the embedded HTTP server.

The server remains a single compiled binary. But now, it grows with your codebase.

Social Authentication Without Cloud Services

User authentication shouldn't require subscribing to third-party identity management platforms that charge per monthly active user.

I added native OAuth2 social login flows for GitHub, Google, and Apple directly into the binary. The engine handles authorization code exchanges, cryptographically signs state tokens, maps profile metadata, and binds social identities to user records. You can configure credentials directly via the REST API or through the moul-ctl terminal console.

Making the Binary Agent-Native with MCP

Modern development increasingly relies on AI coding assistants. Yet most backend runtimes remain opaque black boxes to these tools, forcing developers to manually copy schema definitions, job queue states, or log outputs into context windows.

To make Moul native to AI workflows, I embedded a Model Context Protocol (MCP) server directly into moul. Any AI assistant—whether running locally in Cursor, Antigravity, or via stdio—can query /mcp to inspect database collections, check background worker health, toggle feature flags, and read server CPU metrics.

I also embedded llms.txt and AGENTS.md documentation into the HTTP router. The binary carries its own architectural map. When an AI model interacts with your backend, it doesn't have to guess how the engine works; it reads the documentation directly from the running binary.

Deep Schema Migrations in SQLite

Modifying SQLite schemas at runtime is notoriously fragile. Adding columns is simple; dropping fields or altering column data types requires creating shadow tables, copying existing rows, dropping the old table, and renaming the copy.

I updated the database engine to handle non-destructive column removals and type mutations automatically. When you modify a schema via REST or the TUI, moul builds the migration transaction in memory, verifies foreign key constraints, and executes the table swap cleanly.


Engineering infrastructure is a continuous exercise in finding balance. You want simplicity, but not at the cost of flexibility. You want performance, but not at the expense of developer sanity.

Moul is growing, but its goal remains unchanged: to give developers a quiet, self-contained stack that runs reliably on simple hardware without requiring a cloud operations team.

Footnotes

  1. We renamed the server executable to moul and the TUI management client to moul-ctl.