Self-hosted functions (Lambda-like) runtime and CLI
Project description
fnctl
A tiny, self-hosted functions (Lambda-like) runtime and CLI. Create small functions, run a local HTTP gateway to invoke them via GET, POST, etc., and manage per-function logs.
No heavy dependencies; built on Python standard library. Designed to support Python first and be easily extended to other languages via an exec interface.
Features
- Create, destroy, and list functions:
fnctl create|destroy|list - Run HTTP server:
fnctl serve(routes at/fn/<name>) - Optional per-function logging with
fnctl logs <name> [-f] - Toggle logs per function:
fnctl enable-logs <name>/fnctl disable-logs <name> - Python runtime (module file + handler), plus generic
execruntime (JSON over stdin/stdout) - Minimal and extensible design; no external deps
Quickstart
- Start the server:
fnctl serve --host 127.0.0.1 --port 8080
- Create a Python function:
fnctl create hello --lang python
This scaffolds:
~/.fnctl/
functions/
hello/
fnctl.json # function config
main.py # template with handler(event, context)
logs/
- Invoke it:
curl -s http://127.0.0.1:8080/fn/hello
curl -s "http://127.0.0.1:8080/fn/hello?name=dev"
- List functions:
fnctl list
- Destroy a function:
fnctl destroy hello
# Add --purge-logs to remove its logs
Request/Response Contract
- Request event passed to handler:
method: HTTP methodpath: URL pathquery: dict of query paramsheaders: dict of request headersbody: request body (string)
- Handler return options:
- Lambda-style
{"statusCode": int, "headers": {..}, "body": any} - Any JSON-serializable object (returned as JSON)
- String (returned as
text/plain)
- Lambda-style
Runtimes
- Python: set
entrypointlike"main:handler". The file sits next to the config file. - Exec: set
commandto a shell command. The command receives a single JSON object on stdin:{ "event": ..., "context": ... }and should print JSON to stdout in the same Lambda-style shape.
Example exec template (created by fnctl create <name> --lang exec): handler.sh.
Logging
- Enable/disable per function via config or CLI:
fnctl enable-logs <name>fnctl disable-logs <name>
- Inspect logs:
fnctl logs <name>or follow with-f
- Logs are JSON lines at
~/.fnctl/logs/<name>.log(or/var/lib/fnctl/logswhen using the system service).
System Service (optional)
A systemd unit is included. After installing the Debian package:
sudo systemctl enable --now fnctl.service
# default: listens on 0.0.0.0:8080 and stores data in /var/lib/fnctl
The service runs under a dedicated fnctl user (created by the package).
The installed package sets a system default at /etc/fnctl/config.json and
adds /etc/profile.d/fnctl.sh, so fnctl commands default to /var/lib/fnctl.
Tip: When running locally without installing, you can override the data dir with FNCTL_HOME:
FNCTL_HOME=$(pwd)/.fnctl-local fnctl serve
Packaging (Debian/Ubuntu)
This repo includes Debian packaging under debian/. Build the .deb locally:
# Install build tools once:
sudo apt update && sudo apt install -y build-essential debhelper dh-python python3 python3-setuptools python3-pytest
# Build the package (runs tests via pytest)
dpkg-buildpackage -us -uc -b
# Install the resulting .deb from the parent directory
sudo apt install ./fnctl_0.1.0_all.deb
- To skip tests during build, set
DEB_BUILD_OPTIONS=nocheck.
Testing
- Run all tests with pytest:
pytest -q
-
Tests use a temporary
FNCTL_HOMEso they do not touch your real~/.fnctl. -
The suite covers creating, listing, invoking, and destroying functions (with and without purging logs).
-
Show
print()output during tests (optional):
pytest -s -q # disable capture to see prints live
# or
pytest -q --capture=tee-sys # show prints while also capturing
- During Debian package build, you can surface prints in the build log by overriding the test args:
PYBUILD_TEST_ARGS="pytest -s -q" dpkg-buildpackage -us -uc -b
Hosting an APT repository (so users can apt install fnctl)
To allow apt install fnctl directly, host a signed APT repo and publish releases.
One simple approach is GitHub Pages + reprepro or aptly in CI.
High-level steps:
- Create a GPG key for signing packages and export the public key.
- Use
repreproto create a repo structure and include your built.deb. - Upload the repo to GitHub Pages (or any static host supporting HTTPS).
- Document adding your repo:
curl -fsSL https://YOUR_DOMAIN/apt/pubkey.gpg | sudo gpg --dearmor -o /usr/share/keyrings/fnctl.gpg
echo "deb [signed-by=/usr/share/keyrings/fnctl.gpg] https://YOUR_DOMAIN/apt stable main" | sudo tee /etc/apt/sources.list.d/fnctl.list
sudo apt update
sudo apt install fnctl
You can automate building and publishing from GitHub Actions by:
- Building the
.deb - Running
repreproto add it into the repo - Pushing to a dedicated
gh-pagesbranch
Note:
apt install fnctlworks once your repo is added to the system’s APT sources. Without adding your custom repo, it won’t be in the default Ubuntu/Debian archives.
GitHub Package Page: What to Document
Include the following in the GitHub package/release page:
- What it is: self-hosted function runtime and CLI.
- Installation:
- Add APT repository + GPG key instructions
apt install fnctl- Optional: how to run as systemd service
- Quickstart usage:
fnctl create,fnctl list,fnctl destroy,fnctl serve,fnctl logs - HTTP contract: structure of
event, accepted handler returns - Logging: how to enable/disable, where logs are stored
- Extending languages: describe
execruntime contract (stdin JSON, stdout JSON) - Security notes: functions run locally; review exec commands; network exposure
- Versioning: semantic version; changelog highlights
Directory Layout
- Python package:
fnctl/containing CLI, server, runtime, templates - Debian packaging:
debian/ - Systemd unit:
debian/fnctl.service
Roadmap / Extensibility
- Add websockets/events
- Pluggable auth/middleware for the gateway
- Concurrency and scaling options
- Language shims (Node.js, Go) via
execwrapper
LXC Quick Test
On a clean Ubuntu container (22.04):
lxc launch ubuntu:22.04 fnctl-test
tar czf /tmp/fnctl-src.tgz .
lxc file push /tmp/fnctl-src.tgz fnctl-test/root/
lxc exec fnctl-test -- bash -lc 'cd /root && tar xzf fnctl-src.tgz && cd fnctl'
lxc exec fnctl-test -- bash -lc 'apt-get update && apt-get install -y build-essential debhelper dh-python python3 python3-setuptools python3-pytest'
lxc exec fnctl-test -- bash -lc 'cd /root/fnctl && dpkg-buildpackage -us -uc -b'
lxc exec fnctl-test -- bash -lc 'apt-get install -y /root/fnctl_0.1.0_all.deb'
lxc exec fnctl-test -- bash -lc 'sudo -u fnctl FNCTL_HOME=/var/lib/fnctl fnctl create hello'
lxc exec fnctl-test -- systemctl enable --now fnctl.service
lxc exec fnctl-test -- curl -s http://127.0.0.1:8080/fn/hello
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fnctl-0.1.0.tar.gz.
File metadata
- Download URL: fnctl-0.1.0.tar.gz
- Upload date:
- Size: 14.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b7b10862d64755037ab65731298e3878ac96b157e544d12ddfe6f096c7e3a9df
|
|
| MD5 |
9f8bc96ba5c9f53a0b743cca895ca7a9
|
|
| BLAKE2b-256 |
be4b0108ecc25100bf7843a54430b20a429135d90d282ecf47b96e27821e6072
|
File details
Details for the file fnctl-0.1.0-py3-none-any.whl.
File metadata
- Download URL: fnctl-0.1.0-py3-none-any.whl
- Upload date:
- Size: 12.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
43563c06e2da1312fe75c132d0b18aa17115e32dec5d874e620461e4df84e0a9
|
|
| MD5 |
571e3193267f94e932637c986ba59451
|
|
| BLAKE2b-256 |
ccc59935d78cff99b777c4a0c038257cb1752e399d0dc08413f0c2384df380b8
|