HEART Templates#
Starter code that gives any product a working HEART in minutes
Release v1.2611.3 (November 2026) · 17 templates (14 tested) · 20 more planned · Licence: Apache-2.0
In one minute#
A template is a small, complete program you copy into your product. It already does everything HEART Basic asks for. You change four lines (your product's name and ID) and write one function: "is my product doing its main job?" That's it: your product now has a HEART.
You can always build a HEART from scratch using the standard. Templates just save you the work, and they are tested against the standard on every release.
Every template says exactly which template and which release it is, for example:
heart/embeddedstandard/rust/v1.2611
heart/embeddedstandard/wasm-wat/v1.2611
Licence#
All templates are licensed under the Apache License 2.0 (see LICENSE and NOTICE). You may copy them into any product, open or closed source, change them freely, and sell the result. Keep the licence and notice with any copy of the template files you hand on. Every file carries the line SPDX-License-Identifier: Apache-2.0, so the licence stays clear even when one file is copied on its own. The licence also grants a patent licence from contributors, which matters to companies building hardware.
1. Template names and versions#
heart/embeddedstandard/ rust / v1.2611
───┬── ──┬─────
template version
v1 = HEART standard version 1
2611 = released in 2026, month 11
- The template name says the language or platform:
rust,c-baremetal,asm-arm-cortex-m. - The version is
v+ the HEART major version +.+ the year and month of release (YYMM). Sov1.2611is "HEART 1, November 2026". You can tell how old a template is just by reading its name. - A newer number is always a newer release:
v1.2702(February 2027) replacesv1.2611.
Where the ID appears#
You can always tell which template a system was built from:
| Where | Example |
|---|---|
| In every file, at the top | Template: heart/embeddedstandard/go/v1.2611 |
| In the code, as a constant | const template = "heart/embeddedstandard/go/v1.2611" |
In the system's public Card, in the template field | "template": "heart/embeddedstandard/go/v1.2611" |
The Card field is public, so a tester, a Gateway or the HEART-Rating client can see it without logging in. When you change the template, keep the template value as it is. It records where your code started, not who changed it.
How often templates are updated#
| When | What |
|---|---|
| Every three months (February, May, August, November) | A new release of all templates, such as v1.2702. It brings fixes, the latest rules of the standard, and new templates. |
| When a security problem is found | An urgent release of the affected templates, named after the month it comes out. |
| Always | Old releases stay downloadable, so a product built on v1.2611 can always be compared with what it started from. |
A release is published only if every testable template passes the test harness (Section 6).
2. What every template does#
Every template, in every language, does the same things. This is HEART Basic at Autonomy Level A1 (Parts 2 and 3 of the standard).
- Says who it is. It publishes the HEART Card, including its template ID.
- Says how it is. It publishes the Pulse:
healthy,failingorunknown, with a counter. - Checks its main job every 30 seconds, using the one function you write. It needs no internet and no AI model.
- Raises an alert with fault code
system.main-function-lostwhen the job stops, and resolves it when the job comes back. - Tells a person through an
escalatefunction you connect to your own route: app, email or dashboard. - Answers in the standard way: the
Heart-Versionheader, CloudEvents envelopes, standard error messages,404for unknown paths and501for parts of HEART it does not offer. - Uses the standard port 43278, and falls back to 7685.
Every template comes with a demo: it pretends the main job has stopped while a file named fault.flag exists. Create the file to see the alert, and delete it to see it resolved.
What templates do not do: approvals, privacy filtering above public, logins, or acting on their own. Those are HEART Connected and Full. For those, build on the reference implementation (heart-core-rs) instead.
3. Two families#
Server templates: for anything that runs an operating system#
They serve HEART themselves, over HTTP. Use them in apps, services, gateways, industrial PCs and embedded Linux boards.
Device templates: for microcontrollers#
A microcontroller is too small to serve web requests. Instead it sends one short line per event on its serial port, in the HEART Line v1 format. The bridge (templates/bridge/heart_bridge.py), on a PC, Raspberry Pi or building controller, turns the lines into a full HEART. It can also push them to a HEART Gateway. This is the standard's delegation pattern (HEART Core Standard, Section 8).
microcontroller ──serial──► bridge ──HTTP──► anyone asking "how are you?"
(template) lines (PC, Pi) └──► HEART Gateway (optional)
HEART Line v1: one JSON object per line, at most 1 KB, ending in a newline.
| Line | When | Example |
|---|---|---|
card | Once, at start | {"t":"card","id":"org.example.my-device","name":"My Device","version":"0.1.0","instance":"unit-1","template":"heart/embeddedstandard/c-baremetal/v1.2611","interval":30} |
pulse | Every interval | {"t":"pulse","seq":12,"status":"healthy"} |
alert | When the main job stops | {"t":"alert","code":"system.main-function-lost","severity":"critical"} |
resolved | When it comes back | {"t":"resolved","code":"system.main-function-lost"} |
The device notices problems itself, so it keeps working with no network. The bridge adds what a small device cannot: the time, unique IDs, and serving HTTP. If the device goes quiet for three intervals, the bridge reports it as unknown (the Flatline rule).
4. The templates in this release#
Tested means built and run on Linux by tools/test_templates.py: every reply checked against the official schemas, then rated by the HEART-Rating client. All tested templates score 8.9, Good (provisional). The rating is provisional only because the hands-on tests (Part 9) must be done by you, on your product.
Server templates#
| Template ID | Language | For | Status |
|---|---|---|---|
heart/embeddedstandard/python/v1.2611 | Python 3.8+ | Linux, Raspberry Pi, scripts, services | Tested · 8.9 |
heart/embeddedstandard/javascript-node/v1.2611 | JavaScript (Node 18+) | Services, Electron apps | Tested · 8.9 |
heart/embeddedstandard/go/v1.2611 | Go 1.20+ | Embedded Linux (ARM, RISC-V, MIPS), services | Tested · 8.9 |
heart/embeddedstandard/rust/v1.2611 | Rust (std only, no crates) | Embedded Linux, services | Tested · 8.9 |
heart/embeddedstandard/c-posix/v1.2611 | C99, no heap, one thread | Embedded Linux, Yocto, Buildroot, OpenWrt | Tested · 8.9 |
heart/embeddedstandard/java/v1.2611 | Java 11+ (JDK only) | Industrial PCs, gateways | Tested · 8.9 |
heart/embeddedstandard/php/v1.2611 | PHP 8 | Web back ends, CMS plug-ins | Tested · 8.9 |
heart/embeddedstandard/ruby/v1.2611 | Ruby 3 | Web back ends | Tested · 8.9 |
Device templates#
| Template ID | Language | For | Status |
|---|---|---|---|
heart/embeddedstandard/c-baremetal/v1.2611 | C99, no heap, no stdio | Any microcontroller | Tested on Linux · 8.9. The Cortex-M port compiles but has not run on a chip. |
heart/embeddedstandard/micropython/v1.2611 | MicroPython | ESP32, Raspberry Pi Pico, STM32 | Tested under CPython · 8.9. Not yet on a board. |
heart/embeddedstandard/wasm-wat/v1.2611 | WebAssembly text, hand-written | Browsers, plug-in hosts, WAMR and wasm3 on microcontrollers | Tested in Node · 8.9 |
heart/embeddedstandard/asm-x86-64-linux/v1.2611 | x86-64 assembly | x86-64 Linux, and as the model for other assembly ports | Tested · 8.9 |
heart/embeddedstandard/asm-arm-cortex-m/v1.2611 | ARM Thumb-2 assembly | Cortex-M3/M4/M7/M33 | Untested: no ARM assembler was available |
heart/embeddedstandard/asm-riscv32/v1.2611 | RISC-V assembly (RV32IM) | ESP32-C3/C6, GD32V, SiFive, CH32V | Untested: no RISC-V assembler was available |
heart/embeddedstandard/arduino/v1.2611 | Arduino C++ | Uno, Nano, Mega, ESP32, RP2040 | Untested: arduino-cli was not available |
heart/embeddedstandard/bridge/v1.2611 | Python 3.8+ | The PC or controller next to the devices | Tested with every device template |
Watching devices from anywhere: HEART Hub#
The python and esp32-full templates can report their health to https://heartcortex.ai, or to your own site running the same open code (heart-monitor/). It is off by default: set MONITOR = True (Python) or HEART_MONITOR in menuconfig (ESP32). At first start the device shows a device ID and claim code; its owner registers it, then sees its health at heartcortex.ai/device/<ID>. Only public health data is ever sent. See spec/HEART-Monitor-1.0.md.
Asking the owner (also off by default). A device can ask its owner to approve a change on the site, from anywhere: ask_owner(title, change, apply) in Python, or HEART_MONITOR_APPROVALS in menuconfig for the ESP32, where it works alongside the device's button. The owner approves with a code emailed to them; the site sees only a title and the change. The device acts only on a decision signed by the site's Ed25519 key, which it pinned at its first hello, and at most 300 seconds old (presence method remote-site, API Standard 6.3). Physical changes are offered only with a second setting: HEART_MONITOR_APPROVALS_PHYSICAL (Python) or HEART_MONITOR_APPROVALS_PHYSICAL in menuconfig.
An AI brain on a lease (brain module, new in v1.2611.3)#
The python and esp32-full templates can let an AI model explain their alerts, under a brain lease from a Command Center (API Standard 7.4; spec/HEART-Command-Center-1.0.md). It is off by default: set BRAIN = True (or HEART_BRAIN=1) in Python, or HEART_BRAIN in menuconfig for the ESP32. It needs the monitor on, because the monitor site is also the Command Center, and the site key the device pinned at its first hello signs everything.
- No model keys on the device. The Command Center holds them. The device renews its lease when a third of it is left, and asks for an explanation only while it holds a lease it checked: signed with the pinned key, for this system, at most 300 seconds long, counted on the device's own clock.
- Every answer is checked before it is shown: signed for a request this device sent, under its current lease, by a model the lease names. It is shown to the owner as "Suggested by an AI model: ..." and never acted on. A forged answer is rejected, counted in
rejectedAnswers, and raisesai.answer-rejected. Three in an hour, and the device cuts its own brain. - Brainless is safe. When the lease runs out (even if the link is blocked) or a signed
brain-offarrives, the device is brainless at once: it keeps noticing and alerting, with no AI.GET /heart/v1/brainsays so to anyone, with the reason, and the owner's Pulse showsautonomy.limitedBy: brain-cut. - Restoring needs the owner. After a cut, the brain comes back only once the owner approves
POST /heart/v1/brain/restore: with the button on the ESP32; in Python, on the monitor site (MONITOR_APPROVALS), with a proof sent in the request (actionIdandpresence, methodremote-site), or by creatingbrain-restore.flagat the machine. If approvals are off for the device on its Hub page, Python refuses the request at once with403 presence-requiredand says how to go on, so it never waits unseen. A lease that simply ran out is not a cut. - No auto mode. Neither template runs a local model, so neither has auto mode. In Python,
PUT /heart/v1/brain/offlinewith{"enabled": false}answers the brain view, and{"enabled": true}answers409 conflict.
Tested by tools/test_brain.py (Python, and both templates against the real Command Center in heart-monitor/) and tools/test_full_starter.py (the ESP32 core).
Restart and sleep (lifecycle, new in v1.2611.3)#
Three templates can carry out the lifecycle module (API Standard 13.2 rule 3), so a person can restart a product, or put it to sleep, through HEART. It is off by default in Python and Node, and needs the owner key in all three.
| Template | Operations | How it is carried out | What waits for a person |
|---|---|---|---|
python | restart, shutdown | before_lifecycle() finishes the product's work, then carry_out() starts the program again in place, or ends it | A force restart, and a shutdown (only a person can start it again): run at once with a remote-site proof sent in the request (actionId and presence); otherwise approved by the owner on the monitor site with MONITOR_APPROVALS. Refused at once with 403 presence-required when the owner cannot be asked there: MONITOR_APPROVALS is off, or approvals are off for the device on its Hub page |
javascript-node | restart, shutdown | beforeLifecycle(), then carryOut() starts a new process and ends the old one | A force restart and a shutdown: always refused with 403 presence-required, and a request that sends presence with 403 presence-invalid, as this starter can check no proof. Do them at the device |
esp32-full | restart, sleep (deep, woken by the timer or the button) | heart_port_lifecycle_ready(), then esp_restart() or esp_deep_sleep_start() | A force restart: a press of the device's button |
In all three: turn it on with LIFECYCLE (or HEART_LIFECYCLE=1) in Python and Node; it is always on in the ESP32 starter.
- A graceful restart that cannot finish in time fails, and is never forced. The product's hook says when it is ready. If it is not ready by
timeoutSec(default 60), the Action endsfailedand nothing restarts. - Every answer is an Action with
nextReachableAt: when the system expects to answer again. A shutdown saysnull, with a note. - The Action survives the restart or sleep. Ask for
/heart/v1/actions/{id}afterwards and it sayssucceeded. Python and Node keep it inheart-lifecycle.json, and the ESP32 in RTC memory, which also keeps a Stop through a restart. - The client may name the Action with
actionId(a UUID), so it knows the digest before it sends the request (API Standard 6.3). A reusedactionIdgets409. - Every lifecycle
POST(and Python'sbrain/restore) needs anIdempotency-Keyheader, 8 to 255 characters (API Standard 5.5). A retry with the same key and body gets the first answer and runs nothing; another body gets422 idempotency-conflict. Keys are kept for 24 hours, across restarts. - The Action operations come with it (API Standard 2):
GET /heart/v1/actions(newest first,limitup to 500,cursor),GET /heart/v1/actions/{id}andPOST /heart/v1/actions/{id}/decision.denyandcancelwork on a waiting Action.approveis refused with403 capability-not-allowed, because the owner key is the only caller and the one who asked never approves their own request. The owner approves on the monitor site, or sends a proof with the request. - Refusals: an unknown operation gets
404, one not offered501, unknown fields ordelaySecabove 0400(there is no schedules module), and a second operation while one is under way409.
Try it: create a file named busy.flag and ask for a restart with {"timeoutSec": 2}. It fails, and the program keeps running.
Full starter templates#
These do more than HEART Basic: a small HTTPS server (F1), every signal from a signal table (F2), analysis by a cloud AI model through a Reasoning Gateway (F3), and commands from people, with the device's own button as proof of presence for anything physical (F4).
| Template ID | Language | For | Status |
|---|---|---|---|
heart/embeddedstandard/esp32-full/v1.2611 | C99 core + ESP-IDF 5.2 port | ESP32, ESP32-S3, ESP32-C3, ESP32-C6 | Core tested on Linux · 8.9, plus 30 tests of F2–F4, lifecycle and the brain lease (also under AddressSanitizer). The ESP-IDF port has not yet been compiled or run on a chip. |
5. The full list: planned templates#
These came out of the brainstorm, grouped by where they run. They will be added in later releases, most useful first.
| Template | Language | For | Why it matters |
|---|---|---|---|
rust-heart-core | Rust + the heart-core crate | Embedded Linux | HEART Full from the reference implementation: approvals, Valve, custom commands |
browser-worker | JavaScript + heart-core WASM | Web apps | Full HEART in a browser tab |
docker-sidecar | Container running heartd | Kubernetes, Docker | Add HEART to any service without touching its code |
cpp-posix | C++17 | Embedded Linux, Qt devices | Many product teams work in C++ |
csharp-dotnet | C# (.NET 8) | Windows, industrial PCs | |
kotlin-android | Kotlin | Android apps and devices | |
swift | Swift | iOS, macOS | |
dart-flutter | Dart | Flutter apps | |
typescript-deno | TypeScript | Deno, Bun, edge | |
elixir | Elixir / Erlang | Nerves devices, telecom | |
lua-openwrt | Lua | OpenWrt routers, NodeMCU | |
rust-embedded-nostd | Rust no_std | Cortex-M, RISC-V, ESP32 | |
zephyr | C (Zephyr module) | Zephyr boards | |
freertos | C (FreeRTOS task) | FreeRTOS microcontrollers | |
zig | Zig | Embedded and systems | |
ada-spark | Ada / SPARK | Rail, avionics, defence | Outside the safety function only |
iec61131-st | IEC 61131-3 Structured Text | PLCs (CODESYS, TwinCAT, Siemens) | Factory controllers, on the supervisory side only |
asm-avr | AVR assembly | ATmega, ATtiny | The smallest 8-bit chips |
asm-xtensa | Xtensa assembly | ESP32 (original), ESP8266 | |
mqtt-device | C + MQTT 5 | Devices already using MQTT | Must follow the signed-command rules of Advisory C5 |
6. How templates stay robust#
Every release must pass these checks before it is published:
- Build every template with warnings treated as errors, where the language allows.
- Run it with a simulated fault.
- Check every reply against the official HEART schemas: Card, Pulse, alerts, errors and envelopes.
- Check the behaviour: the fault is noticed, the alert has the right fault code, unknown paths give
404, and parts not offered give501. - Rate it with the HEART-Rating client and the latest metrics. A template may not fail any test.
- Check the ID: every file states its template ID, and the Card's
templatefield matches it.
pip install 'jsonschema[format]'
python3 tools/test_templates.py # all templates this machine can run
python3 tools/test_templates.py rust go # just these
python3 tools/build_catalog.py # writes catalog.json with the results
Templates that cannot be tested on the release machine are clearly marked Untested, with the reason. They are never called tested.
Rules for template authors#
- One file where possible. No dependencies beyond the language's standard library.
- Mark everything the user must change with
CHANGE. - The demo fault is always
fault.flag, so every template can be tested the same way. - Plain HTTP only on
127.0.0.1. On a network, use TLS or a HEART Gateway (API Standard 8.2). - Never put HEART in a safety function or a control loop (API Standard 15). Templates only watch and report.
6a. Tools that come with the templates#
| Tool | What it does |
|---|---|
tools/heart_signals.py | Drafts a signal table (signals.json) from a Modbus register map, a CAN database or a firmware header, then checks, seals and exports it: as heartd property definitions, or as a C table (--out heart_signals.h) for the Full starters. See spec/HEART-Signal-Tables-1.0.md. |
tools/heart_mdns.py | Finds HEART systems on a local network (_heart._tcp), or announces one from its public Card. See Part 4, Section 3.4. |
tools/avahi/heart.service | Announces a Linux device through Avahi. Copy it to /etc/avahi/services/. |
tools/test_templates.py, tools/build_catalog.py | Test every template, and build the catalog |
tools/test_full_starter.py | Tests the Full starter functions F2–F4 of esp32-full on Linux, and its brain lease (--asan for memory checks) |
tools/test_brain.py, tools/fake_cc.py | Tests the brain lease and signed answers in python, and in both templates against the Command Center of heart-monitor/ (--no-site to skip that part) |
7. Choosing a template#
| Your product | Use |
|---|---|
| An app or service on Linux, macOS or Windows | The server template for your language |
| An embedded Linux board (Raspberry Pi, i.MX, Yocto) | c-posix, go or rust |
| An ESP32 on Wi-Fi that should serve its own signals, explain its alerts and take commands | esp32-full |
| A microcontroller with a serial port | c-baremetal, or micropython, arduino, or an assembly port, plus the bridge |
| A web page or a plug-in host | wasm-wat |
| A product that must approve actions, filter private data or run custom commands | Not a template: use heart-core-rs (HEART Full) |
8. Downloading#
The HEART-Rating service also serves the templates (see heart-rating/README.md):
| Endpoint | Gives |
|---|---|
GET /v1/templates | The catalog: every template, its ID, version and test result |
GET /v1/templates/{name} | One template's details |
GET /v1/templates/{name}/{version}.zip | The template as a zip, with the bridge included for device templates |
For example: curl -O https://<service>/v1/templates/rust/v1.2611.zip