https://github.com/dimiro1/lunar
A lightweight, self-hosted Function-as-a-Service platform written in Go with Lua scripting.
https://github.com/dimiro1/lunar
faas faas-platform go golang mithriljs self-hosted
Last synced: 2 months ago
JSON representation
A lightweight, self-hosted Function-as-a-Service platform written in Go with Lua scripting.
- Host: GitHub
- URL: https://github.com/dimiro1/lunar
- Owner: dimiro1
- License: mit
- Created: 2025-11-15T12:08:27.000Z (7 months ago)
- Default Branch: main
- Last Pushed: 2026-04-18T14:42:09.000Z (2 months ago)
- Last Synced: 2026-04-18T15:35:20.756Z (2 months ago)
- Topics: faas, faas-platform, go, golang, mithriljs, self-hosted
- Language: Go
- Homepage:
- Size: 9.18 MB
- Stars: 76
- Watchers: 2
- Forks: 4
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
Awesome Lists containing this project
README
# Lunar (formerly Faas-Go)
[](https://pkg.go.dev/github.com/dimiro1/lunar)
[](https://goreportcard.com/report/github.com/dimiro1/lunar)
A lightweight, self-hosted Function-as-a-Service platform written in Go with Lua scripting.
> **Beta Phase Notice**: This project is currently in beta. New features and changes are actively being developed, but I promise to maintain backward compatibility for all Lua APIs.
## Features
* **Simple Lua Functions** - Write serverless functions in Lua
* **Code Editor** - Monaco Editor with autocomplete and inline documentation
* **HTTP Triggers** - Execute functions via HTTP requests
* **Built-in APIs** - HTTP client, KV store, environment variables, logging, and more
* **AI Integration** - Chat completions with OpenAI and Anthropic, with request/response logging
* **Email Integration** - Send emails via Resend with scheduling support
* **Version Control** - Track and manage function versions
* **Execution History** - Monitor function executions and logs
* **Beautiful Error Messages** - Human-friendly error messages with code context, line numbers, and actionable suggestions
* **Web Dashboard** - Manage functions through a clean web interface
* **API Documentation** - Swagger UI available at `/docs`
* **Lightweight** - Single binary, no external dependencies
## Screenshots
### Dashboard

### Function Editor

### Environment Variables

### Testing Functions

### Execution History

### Version Management

### Version Comparison

### Command Palette

### Error Messages

### AI Request Logs

### Email Request Logs

## Quick Start
### Prerequisites
- Go 1.26 or newer
- `make`
- Chrome or Chromium if you plan to run the E2E test suite
For CLI internals and code generation details, see [cli/README.md](cli/README.md).
### Building from Source
```bash
git clone https://github.com/dimiro1/lunar.git
cd lunar
make build
```
### Running
```bash
./build/lunar
```
For local development, you can also install the optional contributor tools:
```bash
make install-tools
```
This installs `air` for live reload and `goreleaser` for release packaging.
Then start the development server with:
```bash
make dev
```
The application will be available at `http://localhost:3000`.
### First-Time Setup
On first run, Lunar will automatically generate an API key and save it to `data/api_key.txt`. The key will be printed in the server logs:
```
INFO Generated new API key key=cf31cb0cdc7811ca9cec6a3c77579b3ea28c1e4e10d6fc1061ae71788834c21b file=data/api_key.txt
```
When you access the dashboard, you'll be prompted to enter this API key to login. The key is also available in the `data/api_key.txt` file.
### Your First Function
1. Open `http://localhost:3000` and log in with the API key from `data/api_key.txt`.
2. Create a new function named `hello-world`.
3. Paste the sample handler below and save it.
4. Copy the function ID and invoke it:
```bash
curl http://localhost:3000/fn/
```
You should get back a JSON response. After that, open the function's execution history in the dashboard to inspect logs and request details.
## Writing Functions
Functions are written in Lua and must export a `handler` function:
```lua
function handler(ctx, event)
-- ctx contains execution context (executionId, functionId, etc.)
-- event contains HTTP request data (method, path, query, body, headers)
log.info("Function started")
return {
statusCode = 200,
headers = { ["Content-Type"] = "application/json" },
body = json.encode({ message = "Hello, World!" })
}
end
```
### Available APIs
* **log** - Logging utilities (info, debug, warn, error)
* **kv** - Key-value storage (get, set, delete)
* **env** - Environment variables (get)
* **http** - HTTP client (get, post, put, delete)
* **json** - JSON encoding/decoding
* **crypto** - Cryptographic functions (md5, sha256, hmac, uuid)
* **time** - Time utilities (now, format, sleep)
* **url** - URL utilities (parse, encode, decode)
* **strings** - String manipulation
* **random** - Random generators
* **base64** - Base64 encoding/decoding
* **ai** - AI chat completions (OpenAI, Anthropic)
* **email** - Send emails via Resend
### LLM-Assisted Development
Lunar provides an [`llms.txt`](https://llmstxt.org/) file at `/llms.txt` with the complete Lua API reference, including function signatures, parameters, and code examples. You can use this with any LLM-powered coding assistant to get accurate help when writing Lunar functions.
### Example: Counter Function
```lua
function handler(ctx, event)
-- Get current count from KV store
local count = kv.get("counter") or "0"
local newCount = tonumber(count) + 1
-- Save updated count
kv.set("counter", tostring(newCount))
log.info("Counter incremented to: " .. newCount)
return {
statusCode = 200,
headers = { ["Content-Type"] = "application/json" },
body = json.encode({ count = newCount })
}
end
```
### Example: Send Email
```lua
-- Requires RESEND_API_KEY environment variable
function handler(ctx, event)
local data = json.decode(event.body)
local result, err = email.send({
from = "noreply@yourdomain.com",
to = data.email,
subject = "Welcome!",
html = "
Hello, " .. data.name .. "!
",
scheduled_at = time.now() + 3600 -- Optional: send in 1 hour
})
if err then
return {
statusCode = 500,
body = json.encode({ error = err })
}
end
return {
statusCode = 200,
headers = { ["Content-Type"] = "application/json" },
body = json.encode({ email_id = result.id })
}
end
```
### Calling Functions
```bash
curl -X GET http://localhost:3000/fn/{function-id}
curl -X POST http://localhost:3000/fn/{function-id} -d '{"key":"value"}'
curl -X GET http://localhost:3000/fn/{function-id}?name=John
```
## Deployment
### Docker
```bash
# Run the latest release from Docker Hub
docker run -p 3000:3000 -v $(pwd)/data:/data dimiro1/lunar:latest
# Build and run with Docker
docker build -t lunar .
docker run -p 3000:3000 -v lunar-data:/app/data lunar
# Or use Docker Compose
docker compose up -d
```
### Railway
Lunar is ready to deploy on [Railway](https://railway.app):
1. **Connect Repository** - Link your GitHub repository to Railway
2. **Add Volume** - Create a volume and mount it to `/data`
3. **Set Environment Variables**:
- `BASE_URL` - Your Railway public URL (e.g., `https://yourapp.up.railway.app`)
- `API_KEY` - (Optional) Set a custom API key, or let it auto-generate
4. **Deploy** - Railway will automatically detect the Dockerfile and deploy
The Dockerfile is Railway-compatible and will:
- Use Railway's automatic `PORT` environment variable
- Bind to `0.0.0.0:$PORT` for public networking
- Persist data to the mounted volume at `/data`
## Configuration
Lunar can be configured via environment variables:
```bash
PORT=3000 # HTTP server port (default: 3000)
DATA_DIR=./data # Data directory for SQLite database (default: ./data)
EXECUTION_TIMEOUT=300 # Function execution timeout in seconds (default: 300)
API_KEY=your-key-here # API key for authentication (auto-generated if not set)
BASE_URL=http://localhost:3000 # Base URL for the deployment (auto-detected if not set)
```
### Authentication
The dashboard requires authentication via API key. You can:
1. **Auto-generate** (recommended) - Let Lunar generate a secure key on first run
2. **Set manually** - Provide your own key via the `API_KEY` environment variable
API calls can authenticate using either:
- **Cookie** - Automatically handled by the dashboard after login
- **Bearer token** - Include `Authorization: Bearer YOUR_API_KEY` header
Example API call with Bearer token:
```bash
curl -H "Authorization: Bearer YOUR_API_KEY" http://localhost:3000/api/functions
```
Note: Function execution endpoints (`/fn/{id}`) do not require authentication.
## CLI
Lunar ships a command-line client (`lunar-cli`) that is auto-generated from the OpenAPI spec, so it always stays in sync with the API.
### Installation
```bash
go install github.com/dimiro1/lunar/lunar-cli@latest
```
Or download a pre-built binary from the [Releases](https://github.com/dimiro1/lunar/releases) page (`lunar-cli_*` archives).
### AI Agent Skills
AI agent skills require the CLI to be installed first.
Lunar ships built-in skill definitions that teach your AI coding agent how to use the CLI and write Lua functions.
```bash
lunar-cli skills list # show available skills
lunar-cli skills show lunar-cli # CLI command reference
lunar-cli skills show lunar-lua # Lua function authoring guide
```
To install them, ask your agent:
> "Install the Lunar skills from the `lunar-cli skills` command."
### Authentication
```bash
# Start the device authorization flow (opens a browser tab for approval)
lunar-cli --server http://your-lunar-server login
# If the browser does not open automatically, use the printed approval URL and code.
# The token is saved to ~/.config/lunar/config.yaml automatically
# To log out and clear the stored token:
lunar-cli logout
```
You can also skip the login flow and pass a token directly:
```bash
lunar-cli --token YOUR_API_KEY functions list
# Or via environment variable:
export LUNAR_SERVER=http://your-lunar-server
export LUNAR_TOKEN=YOUR_API_KEY
lunar-cli functions list
```
### Configuration
The CLI stores its configuration in `~/.config/lunar/config.yaml`:
```yaml
server: http://localhost:3000
token:
```
Flags and environment variables always take precedence over the config file:
| Priority | Source |
|----------|--------|
| 1 (highest) | `--server` / `--token` flags |
| 2 | `LUNAR_SERVER` / `LUNAR_TOKEN` env vars |
| 3 | `~/.config/lunar/config.yaml` |
### Commands
#### Functions
```bash
lunar-cli functions list [--limit 20] [--offset 0]
lunar-cli functions create --name hello-world --code handler.lua
lunar-cli functions create --name hello-world --code - # read code from stdin
lunar-cli functions get
lunar-cli functions update --name new-name
lunar-cli functions update --cron-schedule "*/5 * * * *" --cron-status active
lunar-cli functions update --disabled
lunar-cli functions delete
lunar-cli functions env --env API_KEY=secret --env DEBUG=true
lunar-cli functions kv --kv counter=0 --kv state=idle
lunar-cli functions kv --kv shared=value --global # write to global KV
lunar-cli functions next-run
```
#### Versions
```bash
lunar-cli versions list
lunar-cli versions get
lunar-cli versions activate
lunar-cli versions delete
lunar-cli versions diff
```
#### Executions
```bash
lunar-cli executions list
lunar-cli executions get
lunar-cli executions logs
lunar-cli executions ai-requests
lunar-cli executions email-requests
```
#### API Tokens
```bash
lunar-cli tokens list
lunar-cli tokens revoke
```
#### LLM Reference
```bash
lunar-cli llms
```
#### Invoke
Execute a function directly without authentication (functions are public by default):
```bash
lunar-cli invoke
lunar-cli invoke --method POST --body '{"key":"value"}'
lunar-cli invoke --method POST --body - # read body from stdin
```
### Keeping the CLI in Sync with the API
The CLI commands are auto-generated from `internal/api/docs/openapi.yaml`. When the API changes, regenerate with:
```bash
cd cli
go generate ./...
go build ./...
```
## Testing
### Go Tests
Run the Go unit tests:
```bash
make test
```
### Frontend Tests (Jasmine)
The frontend uses [Jasmine](https://jasmine.github.io/) for unit testing, running directly in the browser without Node.js dependencies.
```bash
make test-frontend
```
This starts a local Go server and opens the test runner at `http://localhost:8888/test/SpecRunner.html`. Tests cover:
- Route URL generators
- UI components (Button, Badge, Table, Pagination, ...)
### E2E Tests (chromedp)
End-to-end tests use [chromedp](https://github.com/chromedp/chromedp) to run a headless Chrome browser:
Make sure Chrome or Chromium is installed before running them.
```bash
make test-e2e
```
E2E tests cover:
- Login flow
- Page navigation
- Functions list rendering
### Run All Tests
```bash
make test-all
```
This runs Go unit tests and E2E tests. Run `make test-frontend` separately to open the browser-based Jasmine tests.
## Architecture
* **Backend** - Go with standard library HTTP server, SQLite database
* **Frontend** - Mithril.js SPA with Monaco Editor
* **Runtime** - GopherLua for Lua script execution
* **Storage** - SQLite for functions, versions, executions, KV store, and environment variables
### Frontend Dependencies
JavaScript dependencies are vendored in `frontend/vendor/` (no npm required). Versions are managed in the Makefile:
| Library | Purpose |
|---------|---------|
| Mithril.js | SPA framework |
| Monaco Editor | Code editor |
| Highlight.js | Syntax highlighting |
| Jasmine | Frontend testing |
To update dependencies, edit the version variables in the Makefile and run:
```bash
make vendor-js
```
## Contributing
Contributions are welcome! Please feel free to submit issues or pull requests.
## Author
Claudemiro Alves Feitosa Neto
## License
MIT License - see [LICENSE](LICENSE) file for details.