PLANETAI · how the node works
Observe here.
Decide here.
Act here.
PLANETAI is hyperlocal awareness for climate and local challenges, built on distributed design and production and on open-source principles. A node connects everything that measures one place, from a particle sensor to a satellite, into a picture sharp enough to act on at the scale of a house, a street, a city, and passes upward what the models of a bioregion and a planet cannot see from above. This page is the reasoning behind that, and what has been built so far.
The first instrument
Bali first, because the sensor was already there
Node #1 runs in Kuta Selatan and measures particulates. Air is not the mission. A Smart Citizen kit was already on the wall, the public reference network already published, and people there face the same question every morning: is the room better than the street. That made it the cheapest way to test what we actually wanted to know. Can an observation become an action at one address, and can we measure whether it did. Barcelona, Boston and Santiago are the other pilot sites, each with its own sensors, its own portals and its own numbers.
Nothing about air lives in the node’s code. It is a pack: three rules, three index cells, no Python, and a README that says where each threshold came from. The next domain is a folder. The next scale is an adapter.
Indoor PM2.5 at Bayu 2 (indoor) is 42 µg/m³. Outside is worse right now (~68 from 4 nearby public sensors). Keep the windows shut and run the purifier if you have one.
English and Bahasa Indonesia sit in the same rules file. The reply matters more than the alert, because it is how the network finds out whether anything happened.
One number nobody had
ρ, and how long an observation takes to become an action
The Fab City Index measures a place as DIDO × (1 − PITO) × ρ. Two generations of it had no way to measure ρ, so they assumed it was 1: observation always becomes action. Node #1’s first hour says 0.33. Three alerts worth acting on, one acted on, median three minutes.
The Index’s registry lists 32 datasets across its twenty cells. Governance at the community scale has none, because no dataset anywhere records whether people act on what they know about their own street. A node produces that number, in any domain, at any site. You cannot buy it from a sensor company or download it from a portal.
Community · City · Region · Bioregion · Planet. One shape at every scale.
The claims
Four claims about how a place should be measured.
Each one is checkable in the repository. Air is the first domain, not the subject.
01 · Distributed computation
The computer already exists
Every fab lab has a machine idle in a corner. Every household with a sensor already owns a computer. A node is about two thousand lines and two containers, and it runs on a Mac mini or an old laptop. Nobody has to buy infrastructure to join, which is why a network of hundreds is arithmetic rather than ambition. Compute attaches at both ends through the same three contracts: a phone posting readings and actions below, a lab cluster or a partner’s HPC above. Where the thinking happens is a deployment choice.
02 · Local intelligence
The decision is made where it lands
A node runs its own rules against its own data on its own machine. No round trip, no account, no dependency on someone else’s uptime. When the internet drops, a sensor on the LAN keeps reporting and the rules keep firing. Intelligence here is small and situated: SQL that knows this building, and one day a model that knows this valley. It is not a general mind in a data centre renting attention back to you.
03 · Observation to action
A reading that changes nothing is decoration
Most civic sensing stops at the dashboard, and a dashboard is where a good measurement goes to be admired. What a node produces is a message to someone who can act on it, in their language, with a cooldown so it stays worth reading. Then it records whether they did. That record is both the product and the instrument.
04 · Sovereign data
Sovereignty is a data path, not a policy
Raw readings never leave the machine that recorded them. What moves up is hourly means, index cells carrying their own provenance, and, when a model is worth training, weight updates rather than rows. Peer observatories are read as sources and credited, not absorbed. Nothing here needs a cloud provider to work, so owning your data is something you can check in the code instead of a line in a policy.
The building
Three layers, none of which knows what a metric means.
Three layers closing on the Fab City Index, and none of them knows what a metric means. The same shape works at a house, a district, a province, a bioregion. The core moves numbers and evaluates SQL. A pack decides what a number means.
Sense
Anything that emits a number with a time and a place: your hardware, other people’s networks, open data portals, Earth models, people. A reading is (ts, sensor_id, metric, value) and the core never asks what the metric means. A new source is one function.
Observe
Postgres on the node. Hourly means. Every node exposes the same read API, so the observatory is a client rather than a master. Indoor flagged, stale dropped, raw kept beside corrected, gaps left as gaps.
Act
Rules are SQL over the node’s own data. When one is true, one message goes to the people at the address in their language. Agents draft, humans dispatch. Detect, decide, act, measure, and every step carries a timestamp.
Index
Packs declare which cell their SQL feeds. The core evaluates it and polices the provenance: live, partial or mock, never upgraded on the way up, and no live claim before the data supports it. Governance is filled at every node whatever it measures, because ρ comes from the ledger.
Node #1 · bayu-2
Live.
This is the proof, not the product. It runs at Fab Lab Bali on a Mac that was already in the room, reading a kit that was already on the wall. Nothing was bought. Two containers, about two thousand lines, two minutes from the install line to a running node, and a first message on a phone within the hour.
| Status | Polling every 5 minutes |
| Place | Kuta Selatan, Badung, Bali · −8.827, 115.157 · Community scale |
| Domains | Air, heat, coast, land, place (the neighbourhood from OpenStreetMap and Open Buildings, in PostGIS), open-data health, and a daily insight digest: nine packs. The core is domain-blind. |
| Sensor | Three Smart Citizen kits indoors and one on the wall outside; a Meshtastic gateway with a BME680; PM, temperature, humidity, pressure, gas. From the map and from orbit: 2,904 buildings on OpenStreetMap, 7,219 in Google Open Buildings, 37 mapped uses within a kilometre |
| Reference | Bali Air Dispatch public API: the three nearest outdoor stations, stale and indoor rows excluded; above them Copernicus CAMS, Open-Meteo weather and marine, Earth Engine land cover |
| Hardware | Apple Silicon Mac · Postgres + one Python service |
| First alert | 05:19 UTC, 2 Sep 2026 |
| Index | Environmental|Community for Bali, the first non-mock reading that cell has had |
| Operator | Meaningful Design Group with Fab Lab Bali |
Why this case first
A domain earns the first slot by being cheap to test and worth getting right. Air in Bali is both. The public reference network already exists, the sensor was already installed, and the decision people face every morning, whether to open the windows or keep them shut, was one nobody was answering.
It also surfaced the constraint that matters, and it is not technical. Every AirGradient deployed in Bali in 2025 went silent within the year. Sensing networks die of neglect. What is worth building is not the reading but the person whose job is keeping the instrument alive and the message right. A fab lab network is the only kind of organisation shaped to do that at a thousand addresses, because it can print the enclosure locally, install locally, maintain locally, and teach it in Fab Academy.
Containers
Postgres and one Python service. Nothing else.
Lines of code
Cut from 1,479 and twelve containers on the day it was written.
Index cells a node fills
Two Environmental, two Governance. Nine more have a registered source and need an adapter; seven have no source at all.
Clouds required
Runs on hardware you control. The chat app is the one outside dependency, and it's a function.
Four pillars × five scales = twenty cells
Twenty cells, from an address to the planet.
The Fab City Index measures a place from the address up to the planet. A node is not a Community-scale sensor box that reports upward. It ingests at whichever scale the question lives, using three classes of source. Same core, same contracts, a different kind of number.
Community
A device on your LAN, or a public one nearby. Minutes. Smart Citizen, AirGradient, PurpleAir ship. This is the only class that involves hardware.
City & Region
Open data and statistical APIs. Days to months. One CKAN adapter reads Barcelona’s, Boston’s, Santiago’s and Bali’s portals, which is four of the Index’s own sources from one function.
Bioregion & Planet
A global model sampled at your coordinates. Open-Meteo ships: free, key-free, anywhere on earth. Boundary conditions, published downward, never rolled up into a cell.
The missing class
People answering. Four of the five Social cells are empty across the whole registry and no sensor fills them. This is the adapter nobody has written.
What that looks like across the four pilot sites. Different sensors, different portals, the same node:
And the state of the matrix, counted from the Index’s own registry rather than from ambition:
4 cells
Environmental|Community live from your sensors · Environmental|City partial from public reference · Governance|City from portal maintenance · Governance|Community from ρ.
9 cells
Procurement feeds, statistical APIs, hydrology and biodiversity datasets, economic complexity. Registered, documented, unwired. Socrata and ArcGIS adapters are the cheapest next gain.
7 cells
Four of them are Social. One is Economic|City, arguably the Fab City question, and a lab’s own machine log is the closest thing anyone has to it. One is Governance|Community, which is why ρ matters.
A node at one address fills two cells well and contributes to two more. Nobody should call that a planetary observatory. It is the smallest working piece of one, with the ingestion classes for the other scales already present and one adapter each to prove they run. The full matrix →
What a node can measure
The core knows about readings, not about air.
A reading is a time, a source, a metric name and a number, whether it came from a sensor on your wall, a city’s API or a global model. Everything that depends on the domain lives in a pack: what counts as bad, what someone should do about it, which index cell it feeds. Nine ship: air, heat, insight, cold-start, open-data health, coast, earth-engine, place and a worked example. The rest are sketches with the metric names already proposed, waiting for whoever has the problem.
Live · packs/air-quality
Air
pm25 pm10 temp humidity
Is this room better or worse than the street? Ventilate, or keep it shut. Feeds Environmental|Community and |City. Running at node #1.
Live · packs/open-data-health
Open data
datasets_total datasets_fresh_90d
How much of a city's published data was touched in 90 days. A portal nobody updates is an archive, not open data. Feeds Governance|City for any CKAN portal.
Designed
Water
turbidity tds ph water_level rainfall
Is the spring safe this week? Will the tank run dry before the rain? Should the subak divert? A $30 probe on an ESP32 and a rule.
Designed
Energy
power_w energy_kwh grid_up solar_w
Run the kiln now or in two hours? Is the lab about to trip its supply? Did last night's outage reach the fridge? Feeds Economic.
Designed
Fabrication
machine_hours jobs_completed material_kg
Which machine is the bottleneck? Is this lab producing more than it imports? The one domain that speaks directly to PITO → DIDO.
Designed
Noise & mobility
noise_db vehicle_count occupancy
Is the school street too loud to teach in at 8am? Did anything change after the intervention? The Smart Citizen kit already emits noise.
Designed
Comfort & soil
co2 tvoc_index soil_moisture ec
Ventilate the classroom before the CO₂ makes them sleepy. Irrigate or wait. AirGradient already emits the first two.
Designed means the schema takes it and the names are proposed. It does not mean anyone has built it. The first person to ship one gets to fix the names. DOMAINS.md has the full table and what each would need.
Open source · Apache 2.0
Two minutes, plus downloads.
If you can open a terminal and paste a line, you can run a node. The walkthrough was written against the first real install and fixed for every failure it hit: lost exec bits, a port clash with another container, a Docker builder quirk.
# Mac · Linux (x86) · Windows (WSL2) curl -fsSL planetai.fab.city/install | bash # it asks four things: 1. A short name for this node bayu-2 2. Where is it? Kuta Selatan → -8.80, 115.18 · Asia/Makassar 3. What is this node for? a home · a business · a lab · a district 4. Do you have a sensor? Smart Citizen kit 19880, or your username for every kit on the account # then, two minutes each planetai telegram # alerts on your phone; finds your chat id itself planetai test-alert # one fires now, so you see the whole path /act 3 (on Telegram) # you did the thing. that is the first rho. Or the button on the dashboard.
Step 1 · the check
Nothing starts until it can
One table before any question: system, architecture, memory, disk, container runtime, network, ports. Every cross carries the one line that fixes it on that machine. A machine no runtime can be installed on is told so in a sentence, and nothing is downloaded. Run it on its own first: curl -fsSL planetai.fab.city/preflight | bash
Step 2 · four questions
Detected, not configured
A name. A place, typed as words: coordinates, time zone, city and which outdoor reference applies are looked up. What the node is for. A sensor, or none. The answers are written down before anything heavy runs, so a second run offers them back instead of asking again. Safe to run twice.
Step 3 · the loop
See it fire, then close the loop
planetai test-alert makes one fire now. planetai act records that you did something about it. That is your first ρ.
Core, official, community
Everything local lives in a folder.
A pack is a folder that adds a rule, an index cell, or a sensor. It is how a domain arrives, and how a place tunes one. What counts as bad in Kerobokan is not what counts as bad in Poblenou, and the sentence that gets someone to act is different in every language and every building. The air pack is official and shipped. It is also just a folder you can copy. Most useful packs contain no code at all.
Core
fabcity/planetai-node
maintainers · tagged releases
The runtime, the adapters, two domain-blind rules, the three contracts. Knows nothing about any domain. Full review; must not add a container or a cloud dependency.
- semver, additive schema only
- a node updates in place
- deferred pieces carry a written trigger
Official packs
fabcity/planetai-packs
maintainers · optional at install
Whole domains, reviewed like core but optional at install. air-quality is the first and the template. Water and energy are the obvious next two.
- same review bar as core
- versioned independently
- declares the node version it needs
Community packs
your repo, listed in the marketplace
you · listed, not audited
A PR to the marketplace repo with a manifest and a link. We check the manifest parses, the licence is open, and the README says where the rule was learned. Verified means a maintainer read the code and ran it. Unverified is a normal state, not a warning.
- data packs: rules and cells, no code
- code packs: off until you set a flag
- your version, your repo, your name
A pack is a folder
packs/air-quality/ # official: how node #1 works at all pack.yaml # id, author, version, domain, requires rules.yml # SQL + message. ids namespaced monsoon-bali/… cells.yml # SQL → an fci-cells-v0 row for the Index adapter.py # optional. code. off unless you opt in. $ git clone …/planetai-pack-water packs/water $ docker compose restart app $ curl -s localhost:8080/packs
Data before code
Rules and cells are YAML and SQL. They can't reach the network, they run against the node's own database, and anyone who knows their neighbourhood can write one. That is the contribution we want most, and it needs no trust in a stranger's Python.
A pack that ships adapter.py is code, and code runs with the node’s privileges. Those do not load until the operator reads the file and sets PACKS_ALLOW_CODE=1. The node says so, by name, in the log. That bluntness is the security model at this scale. Dressing it up would be worse.
Zero community packs exist today. One worked example ships in the repo. The marketplace opens when there are three real packs to put in it, because an empty marketplace is worse than none.
Limits
What it isn't.
Node #2 is someone else's machine, or someone else's question
Run one.
If you have a sensor and a computer that stays on, you have a node, whatever you measure. If you have a lab, you have ten. Bring a domain nobody has written yet and you bring the more interesting half. Adding a node to the registry is a pull request.