Running Python with Krill Lambdas
When the built-in calculator isn't enough, drop a sandboxed Python script into your swarm. A greenhouse wet-bulb example, wired live.
Krill gives you a Calculation node that does honest, simple arithmetic on a live value — a unit conversion, a scale factor, an offset. It’s the right tool most of the time. But every so often the math outgrows a single formula on a single number, and you need a real programming language. That’s what a Lambda is: your own Python, kept on the Krill server, run in a sandbox, and wired into the swarm like any other node.
This post walks through a small but real example — computing a wet-bulb temperature from a humidity reading and a temperature reading — and shows the thing that makes Krill Krill: I never call the script. I tell it what to watch, and it runs itself.
Where the calculator stops
The Calculation node reads one source and runs it through a formula. Add, subtract, multiply, divide; reference the value, get a number out. Perfect for “convert this Celsius reading to Fahrenheit.”
A wet-bulb temperature is a different animal. It’s the lowest temperature you can reach by evaporating water into the air — the number that tells you how much cooling your evaporative pad, your misters, or a plant’s own transpiration can actually deliver. And it doesn’t fall out of one reading. You need two at once — dry-bulb temperature and relative humidity — fed through a chunk of real math. There’s no single formula on a single value that gets you there.
So we set the calculator aside and reach for a Lambda.
A Lambda is a Python script on the server
Point a Lambda at a script in /opt/krill/lambdas, and Krill runs it when something it observes changes. Here’s the one this demo uses. It reads the two sensors straight off the local server by name, so it doesn’t care what node IDs they were created with, and applies Stull’s well-known wet-bulb approximation:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#!/usr/bin/env python3
"""Compute a wet-bulb temperature (deg F) from the Temperature and
Humidity DataPoints on the local Krill server."""
import math
import requests
import krill.zone.io.krill_auth as krill_auth
BASE = "https://localhost:8442"
def read_sensors():
nodes = requests.get(BASE + "/nodes", auth=krill_auth.KrillPinAuth(),
verify=krill_auth.KRILL_CERT, timeout=10).json()
by_name = {(n["meta"].get("name") or "").lower(): n for n in nodes
if n.get("type", {}).get("type", "").endswith("DataPoint")}
temp = float(by_name["temperature"]["meta"]["snapshot"]["value"])
humidity = float(by_name["humidity"]["meta"]["snapshot"]["value"])
return temp, humidity
def wet_bulb(temp_f, humidity):
"""Stull (2011) approximation. In/out in degrees Fahrenheit."""
t = (temp_f - 32.0) * 5.0 / 9.0 # the formula works in Celsius
tw = (t * math.atan(0.151977 * math.sqrt(humidity + 8.313659))
+ math.atan(t + humidity)
- math.atan(humidity - 1.676331)
+ 0.00391838 * humidity ** 1.5 * math.atan(0.023101 * humidity)
- 4.686035)
return tw * 9.0 / 5.0 + 32.0
def main():
temp_f, humidity = read_sensors()
print("{:.2f}".format(wet_bulb(temp_f, humidity))) # stdout -> the Lambda's value
if __name__ == "__main__":
main()
Whatever the script prints to stdout becomes the Lambda node’s value. That’s the entire contract: read what you need, do your work, print the answer.
Fenced in: the sandbox
These scripts run on your server with access to your data, so Krill runs them under a sandbox. If firejail is installed, Krill uses it automatically:
- a read-only filesystem, scoped to the lambdas directory,
- a private
/tmp, - a 256 MB memory ceiling,
- a hard 30-second timeout.
No firejail, no problem — the script still runs, just without the isolation, and Krill logs that it did. There’s also an opt-in Docker mode. The full threat model, the path-traversal checks, and the install steps are in the dedicated Lambda Executor Security write-up. The short version: only run scripts you trust, install firejail, and Krill handles the fence.
Building it: nodes, then wiring
The swarm is four nodes and two wires:
flowchart LR
H[Humidity<br/>76.2 %] --> L
T[Temperature<br/>85 °F] --> L
L[Wet Bulb Lambda<br/>wet_bulb.py] --> W[Wet Bulb<br/>°F]
style L fill:#2d6cdf,color:#fff
- A Humidity DataPoint and a Temperature DataPoint — the inputs.
- A Wet Bulb Lambda, pointed at
wet_bulb.py. - A Wet Bulb DataPoint — where the result lands.
Then the part that matters. I don’t write a sequence. I wire observers:
- The Lambda’s sources and inputs are the two sensors, and it’s invoked by its sources. When either reading changes, the Lambda wakes up and runs.
- The Wet Bulb DataPoint’s source is the Lambda, also invoked by its source. When the script finishes, the result point reads the answer and stores it.
That’s it. There’s no loop polling the sensors, no controller stepping through nodes in order, no schedule. Each node minds the one thing it watches. Behavior comes from the wiring.
Watching it run
Push the temperature from 85 up to 88, and the Lambda fires the instant the reading lands — runs the Python, and the wet bulb recalculates itself. Nudge the humidity to 80 percent and it runs again. The wet bulb is always exactly one script-run behind your sensors: as fresh as the data, no button to press, no page to refresh.
And here’s why a grower cares about that gap. At 85 °F and 76 % humidity the wet bulb sits down around 78–79 °F — there’s barely any evaporative cooling left in that air, and a plant is feeling the heat far more than the dry-bulb thermometer suggests. The same script, with two more lines, computes a dew point or a vapor-pressure deficit — the numbers a greenhouse genuinely lives and dies by. Wire those to a threshold and a relay and you’ve got a misting system that reacts to what the plants actually feel, not just the temperature on the wall.
The point
A Lambda is the escape hatch for when Krill’s built-in nodes can’t express your logic — and it stays inside the model the rest of the swarm uses. A sensor changes, your Python runs in its sandbox, the answer flows on to whoever’s watching. The whole greenhouse brain — temperature, humidity, wet bulb, and the relays that act on them — lives on one little box on the shelf, and keeps working when the internet doesn’t.
See also: Lambda Python Executors · Lambda Executor Security · Data Points