Skip to content

Owloops/motebase

Repository files navigation

motebase logo

motebase

Tiny self-hosted PocketBase alternative.

CI LuaRocks License: MIT

Features

Dynamic Collections

Create collections with typed schemas at runtime. Auto-generated CRUD endpoints.

JWT Authentication

Register, login, and protect routes. HMAC-SHA256 signing with jti for revocation.

File Storage

Upload files via multipart forms. Protected files with short-lived tokens.

SQLite + Filesystem

Self-contained database with WAL mode. Files on disk with metadata in SQLite.

Tiny Footprint

Small codebase, tiny binary, minimal Docker image.

Pure Lua

No external binaries. Cross-platform via LuaFileSystem.

Comparison

Supabase PocketBase MoteBase
Size ~2GB+ ~50MB ~2MB
Boot time >1s ~1s <100ms
Self-contained No Yes Yes

Installation

Install Script

curl -fsSL https://raw.githubusercontent.com/owloops/motebase/main/install.sh | bash

Static Binary

Available binaries: linux_x86_64, linux_arm64, darwin_x86_64, darwin_arm64

curl -L https://github.com/owloops/motebase/releases/latest/download/motebase-bin-linux_x86_64 -o motebase
chmod +x motebase
./motebase

Docker

docker pull ghcr.io/owloops/motebase:latest

See Deployment for docker-compose with automatic HTTPS.

From Source

# Install dependencies
luarocks --local install luasocket lsqlite3complete lua-cjson
eval "$(luarocks path --bin)"

# Run
./bin/motebase.lua

Usage

# Start server
./motebase

# With options
./motebase --port 3000 --host 127.0.0.1 --db myapp.db --secret my-secret-key

Options

Option Description Default
-p, --port Port to listen on 8080
-h, --host Host to bind to 0.0.0.0
-d, --db Database file path motebase.db
-s, --secret JWT secret key change-me-in-production
--storage File storage directory ./storage
--help Show help message

Environment Variables

Variable Description
MOTEBASE_SECRET JWT secret key
MOTEBASE_DB Database file path
MOTEBASE_STORAGE File storage directory
MOTEBASE_LOG Enable logging (0 to disable)

API

Collections

# Create collection with schema
curl -X POST http://localhost:8080/api/collections \
  -H "Content-Type: application/json" \
  -d '{"name":"posts","schema":{"title":{"type":"string","required":true},"body":{"type":"text"}}}'

# List collections
curl http://localhost:8080/api/collections

# Delete collection
curl -X DELETE http://localhost:8080/api/collections/posts

Records

# Create record
curl -X POST http://localhost:8080/api/collections/posts/records \
  -H "Content-Type: application/json" \
  -d '{"title":"Hello World","body":"My first post"}'

# List records
curl http://localhost:8080/api/collections/posts/records

# Get record
curl http://localhost:8080/api/collections/posts/records/1

# Update record
curl -X PATCH http://localhost:8080/api/collections/posts/records/1 \
  -H "Content-Type: application/json" \
  -d '{"title":"Updated Title"}'

# Delete record
curl -X DELETE http://localhost:8080/api/collections/posts/records/1

Authentication

# Register
curl -X POST http://localhost:8080/api/auth/register \
  -H "Content-Type: application/json" \
  -d '{"email":"user@example.com","password":"password123"}'

# Login (returns JWT token)
curl -X POST http://localhost:8080/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"user@example.com","password":"password123"}'

# Get current user
curl http://localhost:8080/api/auth/me \
  -H "Authorization: Bearer <token>"

Schema Types

Type Description
string Short text
text Long text
email Email with validation
number Numeric value
boolean True/false
json JSON object
file File upload (see File Storage)

File Storage

# Create collection with file field
curl -X POST http://localhost:8080/api/collections \
  -H "Content-Type: application/json" \
  -d '{"name":"docs","schema":{"title":{"type":"string"},"attachment":{"type":"file"}}}'

# Upload file with record
curl -X POST http://localhost:8080/api/collections/docs/records \
  -F "title=My Document" \
  -F "attachment=@document.pdf"

# Download file
curl http://localhost:8080/api/files/docs/1/document_abc123.pdf -o file.pdf

Protected Files

Mark file fields as protected to require token-based access:

# Create collection with protected file
curl -X POST http://localhost:8080/api/collections \
  -H "Content-Type: application/json" \
  -d '{"name":"private","schema":{"doc":{"type":"file","protected":true}}}'

# Get file token (requires auth)
curl -X POST http://localhost:8080/api/files/token \
  -H "Authorization: Bearer <token>"
# Returns: {"token":"...","expires":120}

# Access protected file with token
curl "http://localhost:8080/api/files/private/1/doc_abc123.pdf?token=<file_token>"

Deployment

Docker Compose (Recommended)

The included docker-compose.yml runs MoteBase with Caddy for automatic HTTPS:

# Development (self-signed cert for localhost)
docker compose up -d

# Production (auto Let's Encrypt cert)
DOMAIN=api.example.com MOTEBASE_SECRET=your-secret docker compose up -d

Manual Reverse Proxy

MoteBase runs HTTP without TLS. For production, use a reverse proxy:

# Caddyfile
example.com {
    reverse_proxy localhost:8080
}

Development

./bin/motebase.lua           # Run from source
busted                       # Run tests
luacheck .                   # Lint
stylua .                     # Format

License

MIT

About

Tiny self-hosted PocketBase alternative

Resources

License

Stars

Watchers

Forks

Packages

No packages published