zobox
v0.1.1
Published
Zo-native inbox + sorter + router engine.
Maintainers
Readme
Zobox
Zobox is a Zo-native, open-source inbox + sorter + router engine. It accepts arbitrary structured JSON messages with optional file attachments, stores them durably, and routes them according to configurable sorters.
Features
- Single ingestion endpoint:
POST /messageswith multipart or JSON support - Filesystem-first storage: Envelopes in
inbox/, attachments infiles/, indexed by SQLite - Type-driven sorters: Define types and sorters in
zobox.config.toml - Worker polling:
GET /messages/nextfor building distributed subscribers - Flexible routing: Send messages to webhooks, workers, or store locally
- Path templating: Control attachment storage with
{channel}/{date}/{eventId}/{filename}patterns - Multiple auth modes: Admin and read-only API keys via environment variables
- Lightweight: Bun/Hono server, easy to run as a Zo User Service or standalone
Quick Start
Get Zobox running locally in under 5 minutes:
1. Install Dependencies
bun installOr with npm:
npm install2. Set Up Environment
export ZOBOX_ADMIN_API_KEY="dev-admin-key"
export ZOBOX_READ_API_KEY="dev-read-key"3. Initialize and Start
ZOBOX_ADMIN_API_KEY="dev-admin-key" bunx zobox init --base-dir /home/workspace/InboxThis command:
- Creates the directory structure (
inbox/,files/,db/,logs/) - Copies example config files
- Runs database migrations
- Starts the server
Server listens on http://localhost:8787 by default.
4. Ingest Your First Item
curl -X POST "http://localhost:8787/messages" \
-H "content-type: application/json" \
-H "x-api-key: $ZOBOX_ADMIN_API_KEY" \
-d '{
"type": "update",
"payload": { "text": "First idea" }
}'5. List Items
curl "http://localhost:8787/messages?limit=20" \
-H "x-api-key: $ZOBOX_READ_API_KEY"You should see your item in the response!
Development
Prerequisites
- Bun >= 1.1 (or Node.js >= 18)
- SQLite (included with Bun)
Setup
# Install dependencies
bun install
# Copy example config
cp config/zobox.config.example.toml /home/workspace/Inbox/zobox.config.toml
# Set environment variables
export ZOBOX_ADMIN_API_KEY="dev-admin-key"
export ZOBOX_READ_API_KEY="dev-read-key"
export ZOBOX_BASE_DIR="/home/workspace/Inbox"Running
# Development mode (with hot reload)
bun run dev
# Production mode
bun run start
# Or directly
bun run src/server.tsCLI Commands
# Initialize directory structure, copy configs, run migrations, and start server
bunx zobox init [--base-dir PATH] [--port PORT]
# Start server only (assumes init already done)
bunx zobox serve [--base-dir PATH] [--port PORT]
# Run migrations only
bunx zobox migrate [--base-dir PATH]
# Help
bunx zobox helpNote: The
--base-dirflag always takes precedence overZOBOX_BASE_DIRenvironment variable and anybase_dirvalue in the config file.
Testing
# Run tests
bun run test
# Run tests in watch mode
bun run test:watch
# Lint and check code
bun run lint
# Lint and auto-fix issues
bun run checkGit Hooks
This project uses Lefthook for automated pre-commit and pre-push checks:
Pre-commit hooks (run in parallel):
- format: Auto-format code using Biome
- lint: Check code quality with Biome
- types: TypeScript type checking with
tsc --noEmit - test-related: Run tests when test files or source files change
Pre-push hooks:
- test-all: Run full test suite
- lint-strict: Strict linting with error-on-warnings
Hooks are installed automatically via the prepare script when you run bun install.
Customizing Hooks
To customize hooks for your local environment, copy the example:
cp .lefthook-local.yml.example .lefthook-local.ymlThen edit .lefthook-local.yml to skip expensive checks during fast iteration:
# Skip type checking and tests on commit (faster iteration)
pre-commit:
commands:
types:
skip: true
test-related:
skip: trueYour local customizations won't be committed (.lefthook-local.yml is in .gitignore).
Project Structure
zobox/
bin/
zobox.ts # CLI entrypoint
src/
types.ts # TypeScript type definitions
config.ts # TOML config loader
storage.ts # SQLite + filesystem storage
sorters.ts # sorter and routing logic
server.ts # Hono HTTP server
config/
zobox.config.example.toml
routes.example.json
db/
migrations/
001_init.sql
docs/
API.md # API reference
CONFIGURATION.md # Configuration guideDocumentation
- API Reference: Complete HTTP API documentation with examples
- Configuration Guide: TOML schema, path templates, sorters, and route destinations
Zo Integration
Deploy Zobox as a Zo User Service:
1. Initialize Zobox (One-Time Setup)
ZOBOX_ADMIN_API_KEY="your-admin-key" bunx zobox init --base-dir /home/workspace/InboxThis creates the directory structure, copies config files, and runs migrations. You can stop the server after initialization (Ctrl+C).
2. Create User Service
Configure in Zo:
- Label:
zobox - Type:
http - Local port:
8787 - Entrypoint:
bunx zobox serve - Workdir:
/home/workspace/Inbox
3. Set Environment Variables
Add to your Zo service configuration:
ZOBOX_ADMIN_API_KEY(required)ZOBOX_READ_API_KEY(optional, recommended)
Your Zobox service will start automatically with Zo.
API Overview
See docs/API.md for complete documentation.
Core Endpoints
POST /messages: Ingest messages (JSON or multipart with attachments)GET /messages: List messages with filtering and cursor pagination (response key:items)GET /messages/next: Worker polling for unclaimed messagesPOST /messages/:id/ack: Acknowledge item processingGET /health: Health check
Authentication
# Admin key (full access)
x-api-key: YOUR_ADMIN_KEY
# Read key (read-only)
x-api-key: YOUR_READ_KEY
# Or Bearer token
authorization: Bearer YOUR_KEYConfigure in zobox.config.toml:
[auth]
admin_api_key_env_var = "ZOBOX_ADMIN_API_KEY"
read_api_key_env_var = "ZOBOX_READ_API_KEY"
required = trueIngest Examples
JSON only:
curl -X POST "http://localhost:8787/messages" \
-H "content-type: application/json" \
-H "x-api-key: YOUR_ADMIN_KEY" \
-d '{"type":"update","payload":{"text":"Hello"}}'JSON + base64 attachments:
curl -X POST "http://localhost:8787/messages" \
-H "content-type: application/json" \
-H "x-api-key: YOUR_ADMIN_KEY" \
-d '{
"type":"post",
"payload":{"title":"My post"},
"attachments":[{"filename":"photo.jpg","mimeType":"image/jpeg","base64":"..."}]
}'Multipart with files:
curl -X POST "http://localhost:8787/messages" \
-H "x-api-key: YOUR_ADMIN_KEY" \
-F 'event={"type":"post","payload":{"title":"My post"}}' \
-F '[email protected]'Configuration
See docs/CONFIGURATION.md for complete guide.
Types and Sorters
Define types in zobox.config.toml:
[types.update]
description = "Generic status update"
channel = "Updates"
[sorters.updates]
type = "update"
files_path_template = "{baseFilesDir}/Updates/{date}/{eventId}/{filename}"
append_to_file = "/home/workspace/Inbox/updates.md"
destination = "store_only"Path Templates
Control where attachments are stored using tokens:
{baseFilesDir}: Base files directory{channel}: Item channel{date}: ISO date (YYYY-MM-DD){eventId}: Item UUID{timestamp}: Sanitized timestamp{filename}: Final filename
Example: {baseFilesDir}/{channel}/{date}/{eventId}/{filename}
Renders: /home/workspace/Inbox/files/Updates/2025-11-22/550e8400.../photo.jpg
Filename Strategies
original: Keep original filenametimestampPrefix: Prefix with20251122T123456_eventIdPrefix: Prefix with item UUIDuuid: Replace filename with new UUID
Route Destinations
Define routing in routes.json:
{
"destinations": {
"store_only": {
"kind": "noop"
},
"publish_to_worker": {
"kind": "http",
"url": "http://localhost:9000/zobox/messages",
"method": "POST",
"enabled": true
}
}
}Reference in sorter:
[sorters.posts]
type = "post"
destination = "publish_to_worker"Storage Layout
Given base_dir = "/home/workspace/Inbox":
/home/workspace/Inbox/
zobox.config.toml
routes.json
inbox/
YYYY-MM-DD/
<message-id>.json # Message envelopes
files/
<channel>/
YYYY-MM-DD/
<item-id>/
<filename> # Attachments
db/
zobox.db # SQLite index
migrations/
001_init.sql
logs/ # Reserved for future useSQLite Schema
Table messages:
id,type,channel,created_atfile_path,file_dirattachments_count,has_attachmentssubscribed_by,subscribed_atsummary(reserved for future previews)tags(JSON string)
Indexes on created_at, type, channel, has_attachments, tags for fast queries.
