Hacker News

I gave an agent a lakehouse and control of my garden

The garden

I guess this is what a mid-life crisis looks like. I started gardening. Nothing serious.

I have a small garden at home, and I carved out about two square metres on one side to grow some vegetables. I love seeing things grow and, if I can eat them afterwards, even better. I also thought it would be a nice thing to do with the kids. Although, as I would later discover, the kids were interested for approximately two hours.

This is not an impressive vegetable garden. We’re talking about a narrow raised bed with cabbage, broccoli, lettuce and spinach: the usual things you plant going into winter.

And how did I decide what to plant? Obviously, I asked Claude. How deep should the bed be? Claude. What soil should I buy? Claude. How far apart should the broccoli go? Claude. How much should I water it? You get the idea.

That was probably the moment things started going wrong. Because I started thinking: what else could an agent decide?

First, give the agent some senses

Watering was the obvious one. How much water does a vegetable garden actually need? I could follow a generic schedule (ten minutes every second day, or whatever), but that seemed a bit stupid. Why not measure the moisture in the soil?

So I bought a soil-moisture sensor. Then I thought: what about the water that appears overnight as dew? Leaf-wetness sensor. Temperature? Sensor. Rainfall? Sensor. Solar radiation? Sensor. Wind? Sure, why not.

A few clicks later I had accidentally built a fully instrumented vegetable garden. I went with Ecowitt. The sensors are inexpensive and battery powered, and they all talk wirelessly to a small gateway connected to the internet. Installation is ridiculously easy: put the batteries in, push them into the ground, and they show up on the gateway.

Today there are eleven: eight soil probes, a weather station, a leaf-wetness sensor and the gateway’s own indoor sensor. In total I spent something like €300 on sensors for a vegetable garden that, if everything goes perfectly, might one day produce €40 worth of vegetables. We were already approaching the €60-lettuce phase of the project.

Suddenly I could measure my garden better than many factories measure their production lines.

Of course I mapped the whole thing

I am also a map geek, so there was no chance I was going to leave the sensors as “probe 1”, “probe 2” and “somewhere by the fence”. I opened Google Earth and pinned every soil probe, sprinkler, valve box, the weather station and the camera. Those positions went into a table, garden.sensor_placements, next to the readings.

And yes, the map really is that blurry. Zoom in far enough to find a two-square-metre vegetable bed and Google Earth gives you a handful of pixels. Privacy by smallness.

That immediately makes the questions more interesting. Instead of asking “what is soil-ch6 doing?”, I can ask why the north-west corner of the lawn is always wetter, or which end of the bed dries fastest. Every measurement has a real position, so the agent can reason about the garden in space as well as in time.

And this is where I become completely unreasonable. There is probably nothing I would enjoy more than a live moisture surface across my own lawn: a little gradient from dry to wet, showing what is happening between the probes. Eight probes are obviously not enough to know the moisture at every centimetre, but they are enough to start seeing patterns, test some interpolation and tell me where the next probe should go. Completely unnecessary. Obviously essential.

But there was still one big problem. I couldn’t see it.

So, naturally, I installed a camera

I travel quite a bit. Who was going to tell me whether the plants looked healthy? Certainly not my children.

Now I can watch my two-square-metre farm in glorious 4K, 24 hours a day. Including at night. Which has already produced one important analytical insight: my neighbour’s cat likes to use the vegetable bed as a toilet. Agent-driven agriculture was already paying dividends.

Part II - The lakehouse

Now I had data. A lot of data. At this point I had soil moisture, soil temperature, conductivity, leaf wetness, air temperature and humidity, rain, wind, solar radiation, and pictures of the garden. Great. But now I had another problem: what do I actually do with all of it?

Is 34 % soil moisture good? How quickly is the soil drying? Did yesterday’s watering make any difference? Is the broccoli getting bigger? Should I water today if it might rain tomorrow?

And because I am a data nerd, this time I didn’t ask. I already knew the answer: my vegetable garden needed a lakehouse.

Getting the data out of the garden

The measurements were sitting inside the Ecowitt app, which wasn’t going to help an agent very much. I needed somewhere for the data to accumulate, somewhere an agent could query the history instead of only seeing the latest value.

This is also, more or less, where LakehouseBox started. The first version of the garden ran on Cloudflare: Iceberg tables in R2, a pipeline per table, a small program to receive the weather station’s posts, and a virtual machine on Google Cloud only to relay the camera’s uploads. It worked, and it was too many pieces for a vegetable bed.

What I wanted was a small lakehouse where data accumulates permanently and where I can simply give an agent access to it, without standing up a warehouse, a database server or a collection of little services around it. That became LakehouseBox. How I built it is another post.

The requirement here was simple: a place where my agents could put the data, analyse it, reason over it, look at the pictures, send me an email and adjust the water. And I wanted all of it to be sovereign, running on European infrastructure. My garden is going to be sovereign. No one is going to touch my lettuce.

What I asked the agent Use the LakehouseBox CLI. Create a catalog called garden, create the tables we need for Ecowitt telemetry, and expose a device sink using the Ecowitt preset. Keep it simple: I want the gateway to POST directly into the lakehouse, with no service of mine running in the middle.

lhbox catalog create garden
lhbox sink create --catalog garden --table garden.sensor_readings --name weather_in \
  --device --preset ecowitt --strip PASSKEY

A device sink is a private address that turns each post into rows of a table. The nice thing about the Ecowitt gateway is that it can post its measurements to a server of your choice, so I pasted the address the agent gave me into the Ecowitt app and pressed Save. Done.

A minute later soil moisture, temperature, conductivity, rain, wind, solar radiation and everything else were landing in an Apache Iceberg table. The gateway sends 62 rows per post, and when I added four more soil probes there was nothing to change: the new channels simply started arriving. Since 18 September the garden has collected a little over four million readings.

And now this is great, because I don’t have to look at any boring dashboards. I can just ask:

  • How has the soil moisture changed over the last 24 hours?
  • Which part of the garden dries fastest?
  • Did this morning’s watering make any difference?
  • Is the lawn getting too much water?

That is much more useful to me than another dashboard full of charts. Each of those questions turns into a query over the same tables. “Did this morning’s watering reach the soil?”, for example, is this:

SELECT sensor_id, round(avg(metric_value) FILTER (WHERE event_time = TIMESTAMPTZ '2026-10-23 06:00+00')) AS after_pct
FROM garden.garden.sensor_readings
WHERE sensor_type = 'soil' AND metric_name = 'moisture'
  AND event_time BETWEEN TIMESTAMPTZ '2026-10-23 05:00+00' AND TIMESTAMPTZ '2026-10-23 08:00+00'
GROUP BY 1 ORDER BY 1;

On that morning every lawn probe jumped (one from 29 % to 72 %) except soil-ch5, the dry corner the agent keeps complaining about. I never write that SQL. The agent does.

But the agent was still missing something

Numbers don’t tell you everything. A sensor can tell me the soil is at 31 % moisture. It can’t tell me that one of the broccoli plants has collapsed. That’s what the camera is for: it takes a full-resolution picture of the bed dozens of times a day.

There was one small problem. The camera is a fairly normal security camera. It doesn’t know anything about Iceberg or S3 or agents. It knows FTP. And LakehouseBox didn’t support FTP. So we added it. Not because I had suddenly decided that a lakehouse needed an FTP feature. Because my camera wouldn’t send its pictures any other way.

What I asked the agent My Reolink camera can upload periodic images over FTPS. I don’t want a Raspberry Pi or another server at home. Add a write-only drop box to the garden catalog so the camera can upload directly, and automatically index every image into a table I can query.

lhbox catalog uploader create garden --name camera --prefix camera/rlc-510wa-01/ \
  --protocols ftps --save camera.json

It turned out not to be such a strange requirement. Cameras use FTP. Scanners use FTP. Plenty of industrial equipment uses FTP. Many devices know perfectly well how to produce useful data and have absolutely no idea what an S3 API is.

Now the camera drops a JPEG into LakehouseBox dozens of times a day. The picture stays a picture, a file in the catalog’s storage, and a row appears for it in garden.images with the capture time, the file name, the size and the dimensions:

captured_at camera_id size_bytes width height
2026-10-23 08:06:00 rlc-510wa-01 885,553 2560 1920
2026-10-23 08:00:57 rlc-510wa-01 941,290 2560 1920
2026-10-23 07:05:58 rlc-510wa-01 925,050 2560 1920

So the garden now produced telemetry and a visual history, in the same place, and an agent could ask for “the picture closest to yesterday at noon” with one query.

The camera missed the important thing

A few days later something started eating one of the plants. Holes were appearing in the leaves and I suspected snails. The camera had already caught the neighbour’s cat at night, so surely if something was attacking the lettuce, it would catch that too. It didn’t. I went through the camera’s history and found nothing useful.

So I took a few close-ups with my phone and gave those to the agent instead. That changed the diagnosis completely. On the underside of the leaves it spotted fresh dark specks right next to the chewed areas: frass, which is a polite word for caterpillar droppings. The animal itself was still almost impossible to see, but the evidence that something was actively feeding was there.

That was a useful failure. The camera is perfectly good for seeing whether a plant has collapsed, whether the soil looks wet or whether a cat has visited. But a tiny hole in a leaf is a few pixels of the full frame. Having a camera is not the same thing as giving the agent the right view of the problem.

So I changed the agent. Instead of only looking at a few full frames, it now also works with full-resolution crops of the bed and checks the leaves explicitly for holes, chewed margins, frass, eggs, caterpillars, aphids, slug trails, yellowing and mildew. It also compares each crop with the previous days, because damage is a rate, not a state.

What I changed in the agent's prompt Judge the plants from the bed crops as a separate pass. Look explicitly for holes, chewed margins, frass, caterpillars, eggs, aphid colonies and slug trails. Compare with previous days before deciding the damage is new.

This is one of the things I like most about the setup. When the garden surprises me, I don’t build another application. I improve the agent’s instructions, give it a better view or a better tool, and the next morning the same routine knows how to look for the problem.

My agent wants to talk about the weather

Current measurements tell me what the garden is doing. Watering decisions also depend on what is about to happen. If 16 mm of rain is due on Friday, that matters quite a lot when deciding whether to water on Thursday. So naturally I added predictive analytics. By predictive analytics I mean I read the public weather forecast. But “predictive analytics” sounds much better in an architecture diagram.

What I asked the agent Create a scheduled SQL sink that fetches the public forecast before the garden agent runs each morning and keeps a garden.forecast_daily table up to date. No cron machine and no separate ETL service.

lhbox sink create --catalog garden --table garden.forecast_daily --name forecast_daily \
  --sql @forecast.sql --every 1d --offset 330m --create-table

Every morning at 05:30 UTC, LakehouseBox runs one DuckDB query that reads the public Open-Meteo forecast for the garden (seven days back and seven ahead, with rain, temperature and evapotranspiration) and commits the result to an Iceberg table. Each run keeps the time the forecast was issued, so I can also see how the forecast changed its mind. Official AEMET observations from a station nearby go into garden.reference_readings, as a check on what actually happened.

Again, I didn’t want another little server whose only job is to wake up, fetch a JSON file and put it somewhere. I just wanted tomorrow’s weather to be there when the agent needs it.

Part III - The agent

I still had to remember to ask At this point I could ask pretty much anything about the garden. Great. Except I still had to remember to ask. And the whole point was that I don’t want another system to check every morning.

So I registered an agent inside LakehouseBox (hosted agents are in preview) whose entire job is to look after the garden, and scheduled it for 08:45, just after the morning watering, so it can see whether the water reached the soil.

What I asked the agent Create a hosted agent called garden_morning. Every morning at 08:45, inspect the garden catalog, compare the last day with previous days, look at the watering response and the

Read on Hacker News ↗ ← Back to News

Comments

No comments yet. Start the discussion.