A lightweight Function-as-a-Service platform written in C, built around:
- A multi-threaded HTTP gateway
- A worker pool communicating through Unix domain sockets
- Dynamic route configuration stored in SQLite and synchronized in memory
- Metrics-driven scheduling (CPU, memory, I/O score)
- Why This Project
- Core Features
- Architecture
- Repository Layout
- Requirements
- Quick Start
- Build and Run
- Runtime Matrix
- API and Routes
- Dynamic Function Upload
- Benchmarking
- Troubleshooting
- Authors
- Roadmap
- License
This repository explores a practical low-level FaaS runtime with a focus on:
- High control over performance-critical paths in C
- Simple deployment model on Linux environments
- Fast local routing using in-memory lookup
- Runtime flexibility (WASM and PHP execution paths)
- Smart worker selection using a score model:
score = 0.5 * cpu + 0.3 * mem + 0.2 * io
- Zero-copy style client handoff to workers via FD passing (
SCM_RIGHTS) - Dynamic route updates from SQLite to in-memory KV without gateway restart
- Background metrics ingestion from workers every 500 ms
- Upload endpoint to compile/register functions from descriptor + source
High-level request flow:
Client HTTP request
-> Gateway (HTTP parse + route lookup + scheduler)
-> Selected Worker (Unix socket)
-> Runtime execution (PHP, WASM, or native binary)
-> Direct response to client
Background subsystems:
kv_sqlite_sync: periodic synchronization fromfaas_meta.dbinto RAMmetrics_collector: receives worker metrics via datagram socket
Detailed architecture documentation:
ARCHITECTURE.md
.
├── src/
│ ├── gateway.c # HTTP gateway + scheduler + upload handling
│ ├── worker.c # Worker runtime execution
│ ├── fd_passing.c/.h # sendfd/recvfd over Unix sockets
│ ├── http_handler.c/.h # HTTP parsing, responses, multipart parsing
│ ├── kv.c/.h # In-memory key-value store
│ ├── kv_sqlite_sync.c # SQLite -> KV synchronization thread
│ ├── metrics_*.c/.h # Metrics collection and smoothing
│ └── faas_compiler.c/.h # Upload compiler pipeline
├── pages/
│ └── upload.html # Upload UI served at GET /upload
├── examples/
│ ├── hello.c
│ └── descriptor.json
├── benchmarks/
│ ├── run_benchmark.sh
│ ├── quick_test.sh
│ └── analyze_results.py
├── init.sql # Initial route data
├── Makefile # Canonical build and run targets
├── start.sh # Scripted build + run helper
└── stop.sh # Stop workers/gateway and cleanup sockets
- Linux (native, WSL, container)
- GCC toolchain (
build-essential) make- SQLite runtime and headers (
sqlite3,libsqlite3-dev) - Runtime/toolchain requirements for executing uploaded functions:
phpCLI for PHP routesgccfor C uploads (native binary build)g++for C++ uploads (native binary build)
- Optional:
wasmerfor WASM execution in workers
Install base dependencies on Ubuntu/Debian:
sudo apt-get update
sudo apt-get install -y build-essential make sqlite3 libsqlite3-devAlso recommended for upload workflows:
sudo apt-get install -y php-cli# 1) Build binaries
make
# 2) Initialize SQLite route table
make init
# 3) Start workers + gateway
make startGateway listens on http://localhost:8080.
Web UI is available at:
http://localhost:8080/http://localhost:8080/upload
Stop everything:
make stopmake clean
make
make startchmod +x start.sh stop.sh clean.sh
./start.shmake # build binaries in build/
make init # create and seed faas_meta.db
make start # build + init + launch workers and gateway
make stop # stop worker/gateway processes and cleanup sockets
make clean # remove build artifacts and runtime files| Descriptor Runtime | Build Output | Worker Execution Path | Required Tools |
|---|---|---|---|
php |
source file | php <file> |
php |
wasm |
.wasm module |
wasmer run <module> |
wasmer |
c |
native binary (module.bin) |
direct exec |
gcc |
cpp / c++ |
native binary (module.bin) |
direct exec |
g++ |
rust, go, tinygo, python |
toolchain-dependent | currently mapped to wasm flow | corresponding toolchain |
From init.sql:
POST /resize(PHP demo)GET /ping(PHP demo)POST /api/hello(PHP demo)GET /api/info(PHP demo)GET /python(WASM example, requireswasmerand module)
curl -X POST http://localhost:8080/api/hello \
-H "Content-Type: application/json" \
-d '{"name":"Alice"}'
curl http://localhost:8080/api/infofunctions table schema (k, v, updated):
k: route key in the formatMETHOD:/pathv: JSON descriptor (runtime,module,handler, etc.)updated: Unix timestamp used by sync thread
Upload endpoint:
GET /andGET /uploadserve the HTML upload pagePOST /uploadexpects multipart form-data with:- field
code - field
descriptor
- field
Compilation path:
- temporary input directory:
/tmp/progfile - output path:
/opt/functions/<uuid>/module.binforc/cpp/opt/functions/<uuid>/module.wasmfor wasm pipeline
The compiler component (src/faas_compiler.c) updates deployment metadata and inserts route config into faas_meta.db when available.
Example upload test with repository files:
- In the UI, upload:
examples/hello.cexamples/descriptor.json
- After success, call returned route like:
curl -X POST http://localhost:8080/api/<generated-id> -d '{"name":"Alice"}'Quick benchmark:
cd benchmarks
chmod +x *.sh
./quick_test.shFull benchmark workflow:
cd benchmarks
./run_benchmark.sh
python3 analyze_results.pyMore details:
benchmarks/BENCHMARKING_GUIDE.mdPERFORMANCE.md
Two common linker/build issues fixed in this repository:
- Missing object files in script build commands
- Worker must link
fd_passing.oandhttp_handler.o - Gateway must link
fd_passing.o
WEXITSTATUSunresolved in compiler unit
src/faas_compiler.cneeds#include <sys/wait.h>
If you still get build errors, use:
make clean
makeThen verify dependencies:
gcc --version
sqlite3 --version
ls /usr/include/sqlite3.hsudo lsof -ti:8080 | xargs -r kill -9
make stopThis usually means startup failed early (port conflict, missing dependency, or stale state).
Use this clean recovery sequence:
make stop
make clean
make
make init
XDEBUG_MODE=off make startIf you see errors like:
execlp wasmer: No such file or directoryCould not open input file: /opt/functions/...
Recover with a full reseed + restart so routes point to the local demo files:
make stop
make init
make startThe default seeded routes now use examples/*.php for immediate local testing.
If PHP prints lines like:
Xdebug: [Step Debug] Could not connect to debugging client...
Run gateway with Xdebug disabled:
XDEBUG_MODE=off make startCompilation may succeed while runtime execution fails if these are absent:
wasmerfor WASM modulesphpfor PHP modules
Check quickly:
command -v wasmer || echo "wasmer not installed"
command -v php || echo "php not installed"sed -i 's/\r$//' *.sh benchmarks/*.sh
chmod +x *.sh benchmarks/*.sh- Thread pool in gateway to reduce thread-creation overhead
- Better structured JSON parsing and validation
- Authentication and rate limiting middleware
- CI with repeatable integration tests
