Skip to content

henriquemafra/dropfunctions

Repository files navigation

DROP↓FUNCTIONS

A lightweight, self-hosted edge functions runtime built on Bun. It is written in pure Vanilla JS and currently ships with only one production dependency: jose for JWT verification.

Comes with a built-in dashboard for managing functions, viewing logs in real-time, handling API tokens, and tweaking every setting from a clean UI.

Warning

DropFunctions is still under active development. Expect rough edges, missing polish, and bugs. There are no official releases yet, so for now you should treat the repository as pre-release software.

DropFunctions demo

---

Why?

Most serverless platforms are heavy. You need accounts, CLI tools, YAML files, deploy pipelines. Sometimes you just want to spin up a function and call it from your app. That's what DropFunctions is for.

  • Native JS & TS — drop a .js or .ts file into /functions. Bun handles loading and TypeScript transpilation instantly. No tsc, no Webpack, no build step.
  • The "Guillotine" isolation model — each function runs in its own Worker Thread with a hard timeout watchdog. If user code hangs or loops forever, the worker is terminated and the runtime plus dashboard stay alive.
  • Ultra-lightweight — the runtime is intentionally small and built to be auditable. You can understand the core in an afternoon instead of reverse-engineering a framework.
  • Mobile-first dashboard — manage functions, inspect real-time logs over SSE, edit code, and revoke tokens from a phone without needing a desktop control panel.
  • Minimal production footprint — Bun runtime, Vanilla JS codebase, SQLite for local state, and only one production dependency: jose.
  • Docker ready — production and development compose files are included.

Quick Start

With Bun (local)

# Install Bun if you haven't: https://bun.sh
curl -fsSL https://bun.sh/install | bash

# Clone and install
git clone https://github.com/henriquemafra/dropfunctions.git
cd dropfunctions
bun install

# Copy the env template and edit it
cp .env.example .env

# Start the server
bun run dev

The runtime starts on port 3000 and the dashboard on port 4000.

bun run dev is the full development mode and restarts the app when you edit the runtime or dashboard code.

With Docker

cp .env.example .env
# Edit .env — at minimum change DASHBOARD_PASSWORD and DASHBOARD_SECRET

# Production
docker compose up -d

# Development (with hot-reload and source mounting)
docker compose -f docker-compose.dev.yml up

Writing Functions

Create a .js or .ts file in the functions/ directory. That's it — the filename becomes the route.

functions/hello.js

export default async function(req, ctx) {
  const name = new URL(req.url).searchParams.get('name') || 'world'
  return Response.json({ message: `Hello, ${name}!` })
}

Call it:

curl http://localhost:3000/functions/hello?name=John
# → {"message":"Hello, John!"}

The repo also includes example functions in both JavaScript and TypeScript:

  • functions/firebase-example.js and functions/firebase-example.ts
  • functions/supabase-example.js and functions/supabase-example.ts

The req and ctx objects

Your function receives two arguments:

Argument What it is
req Standard Request object
ctx.user Decoded JWT claims (when auth is enabled)
ctx.env Environment variable accessor
ctx.log Structured logger (ctx.log.info(...), .warn(...), .error(...))
ctx.requestId Unique ID for this request
ctx.functionName Name of the current function

Function Config

Export a config object to customize behavior per function:

export default async function(req, ctx) {
  // your code
}

export const config = {
  auth: true,           // require authentication (overrides global AUTH_REQUIRED)
  timeout: 10000,       // custom timeout in ms (overrides DEFAULT_TIMEOUT_MS)
  rateLimit: {
    requests: 50,       // max requests per window
    window: '5m',       // time window (30s, 5m, 1h)
  },
}

Request Flow

Every request goes through this pipeline:

Request → CORS → Route Match → Logger → Auth Check → Rate Limit → Execute → Response

If any middleware rejects (bad token, rate exceeded, etc.), the chain stops and returns the appropriate error response. Functions that don't exist return 404.


Authentication

DropFunctions supports three auth modes:

No auth (default)

AUTH_PROVIDER=none
AUTH_REQUIRED=false

All functions are public. Simple.

Supabase

AUTH_PROVIDER=supabase
AUTH_REQUIRED=true
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_JWT_SECRET=your-jwt-secret

Clients send a Supabase JWT in the Authorization header. The runtime verifies it and populates ctx.user with the decoded claims.

Firebase

AUTH_PROVIDER=firebase
AUTH_REQUIRED=true
FIREBASE_PROJECT_ID=your-project-id

Same idea — Firebase ID tokens are verified against Google's public keys.

API Tokens

The dashboard lets you create API tokens (prefixed with of_). These work alongside any provider — the runtime checks for of_ tokens first, then falls back to the configured provider.

Tokens can be:

  • Scoped to specific functions
  • Set to expire (30 days, 90 days, 1 year, or never)
  • Revoked at any time from the dashboard
curl -H "Authorization: Bearer of_abc123..." http://localhost:3000/functions/hello

Rate Limiting

Enabled by default. Tracks requests per IP per function.

RATE_LIMIT_ENABLED=true
RATE_LIMIT_REQUESTS=100     # max requests
RATE_LIMIT_WINDOW=1m        # per time window (30s, 5m, 1h)

When a client exceeds the limit, they get a 429 Too Many Requests response. Individual functions can override these limits via their config export.


Environment Variables

Full reference — all optional, sensible defaults are used when not set.

Server

Variable Default Description
PORT 3000 Runtime server port
FUNCTIONS_DIR ./functions Where your function files live
NODE_ENV production Environment mode
DEFAULT_TIMEOUT_MS 5000 Max execution time per function (ms)

Auth

Variable Default Description
AUTH_PROVIDER none none, supabase, or firebase
AUTH_REQUIRED false Require auth globally
SUPABASE_URL Your Supabase project URL
SUPABASE_JWT_SECRET Supabase JWT signing secret
FIREBASE_PROJECT_ID Firebase project identifier

Rate Limiting

Variable Default Description
RATE_LIMIT_ENABLED true Toggle rate limiting on/off
RATE_LIMIT_REQUESTS 100 Max requests per window
RATE_LIMIT_WINDOW 1m Window duration (30s, 5m, 1h)

CORS

Variable Default Description
CORS_ORIGIN * Allowed origins (comma-separated or *)
CORS_METHODS GET,POST,PUT,DELETE,OPTIONS Allowed HTTP methods

Logging

Variable Default Description
LOG_LEVEL info debug, info, warn, error
LOG_FORMAT json json or pretty

Dashboard

Variable Default Description
DASHBOARD_PORT 4000 Dashboard UI port
DASHBOARD_USERNAME admin Login username
DASHBOARD_PASSWORD admin Login password
DASHBOARD_SECRET change-me-in-production Session cookie signing key
DASHBOARD_SESSION_TIMEOUT 1440 Session duration in minutes (default 24h)

Dashboard

The dashboard runs alongside the runtime and gives you a full management interface.

Pages

  • Functions — list, create, edit (with syntax highlighting), delete, enable/disable functions
  • Logs — real-time log viewer with SSE streaming, filter by level/period/search, export and clear
  • Tokens — create and revoke API tokens with optional scoping and expiration
  • Environment — view and edit all .env variables grouped by category
  • Metrics — request counts, latency percentiles (p50/p95/p99), per-function breakdown
  • Settings — runtime configuration without touching files
  • Docs — full configuration reference built into the UI

First-time Setup

On first login, the dashboard shows a guided setup wizard that walks you through:

  1. Server configuration (port, functions directory, timeout)
  2. Authentication provider setup
  3. Rate limiting
  4. CORS settings
  5. Dashboard credentials

The wizard writes everything to .env so you don't have to edit files manually.

Security

  • Sessions are signed with HMAC-SHA256
  • Passwords are never stored in the database
  • The dashboard is completely separate from the runtime — different port, different auth system
  • All API routes require a valid session (except login/logout)

Project Structure

dropfunctions/
├── src/
│   ├── index.js              # Server entry point
│   ├── loader.js             # Function file loading + hot reload
│   ├── router.js             # Route matching (/functions/:name)
│   ├── runner.js             # Execution with timeout
│   ├── auth/
│   │   ├── firebase.js       # Firebase token verification
│   │   └── supabase.js       # Supabase JWT verification
│   ├── middleware/
│   │   ├── auth.js           # Auth middleware
│   │   ├── cors.js           # CORS handling
│   │   ├── logger.js         # Request logging
│   │   └── rateLimit.js      # Rate limiter
│   └── utils/
│       ├── env.js            # Env var helper
│       ├── response.js       # Response utilities
│       └── timeout.js        # Promise timeout wrapper
├── dashboard/
│   ├── server/               # Dashboard API (Bun.serve)
│   │   ├── index.js
│   │   ├── db.js             # SQLite + SSE event bus
│   │   ├── middleware/
│   │   └── routes/
│   └── ui/                   # SPA frontend (vanilla JS)
│       ├── index.html
│       ├── app.js
│       ├── style.css
│       └── pages/
├── functions/                # Your function files go here
├── data/                     # SQLite database (auto-created)
├── Dockerfile
├── docker-compose.yml
├── docker-compose.dev.yml
├── .env.example
└── package.json

Docker

Production

docker compose up -d

This builds the image, mounts ./functions and ./data as volumes, exposes ports 3000 and 4000, and includes a health check.

Development

docker compose -f docker-compose.dev.yml up

Mounts the src/ directory too so you can edit runtime code with hot reload via bun --watch.

Build your own image

docker build -t dropfunctions .
docker run -p 3000:3000 -p 4000:4000 -v ./functions:/app/functions -v ./data:/app/data --env-file .env dropfunctions

Tech Stack

Component Technology
Runtime Bun
Database SQLite (via bun:sqlite)
JWT jose
Dashboard Vanilla JS SPA
Editor Ace Editor (CDN)
Fonts Inter, JetBrains Mono, Space Grotesk

No frameworks. No build step. No transpilation. Just JavaScript.


License

MIT

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Languages