HEART Security Advisory HEART-2026-001#
Five critical flaws and one high-severity flaw in the HEART API Standard 1.0 draft, and how they are fixed
Author: Milind K. Patil, Syncaissa Systems Inc. Date: 8 November 2026 Affects: HEART API Standard 1.0, working draft 1, before 8 November 2026, and the reference implementation heartd 0.1.0 Status: Fixed in the standard, and in heartd (the reference implementation) on 8 November 2026. Track: Intermediate: "In one minute", "How to read this advisory", C3, C4, C5 and H1. Advanced adds C1 and C2. Sections 7 to 9 are background.
In one minute#
A security review of the HEART standard found five critical flaws and one high-severity flaw. None needed a bug in anyone's code. Each was a gap in the rules themselves, so every product built exactly to the standard would have had it.
| # | Flaw | Worst outcome | Severity |
|---|---|---|---|
| C1 | An AI agent holding a person's login counts as that person | The AI approves its own risky actions | Critical |
| C2 | Power can be gained through "standing orders" | An AI or another system sets up a rule that later switches machines on without anyone approving | Critical |
| C3 | Login tokens can be captured and reused elsewhere | One bad device steals logins and uses them on every other device | Critical |
| C4 | Devices have no trustworthy identity | An impostor on the local network pretends to be a device and collects logins and commands | Critical |
| C5 | MQTT commands are not authenticated one by one | Anyone allowed to publish on the broker can send, or replay, commands to devices | Critical |
| H1 | Receipts can leak private values | Someone who can read receipts guesses the private data they describe | High |
All six are now fixed in the API Standard. The fixes are tested by the HEART-Rating tool (heart-rating/ in this repository), so anyone can check whether a product has them.
The reference implementation, heartd, was written before these fixes. Tested against them it failed C1 and C3, and did not sign its Card (C4), and was rated Unsafe (3.9). It has since been updated and now passes every automated security test (Section 9).
How to read this advisory#
For each flaw:
- What was wrong: the gap in the rules, in plain words.
- How it could be attacked: a short, realistic story.
- The fix: what the standard now requires.
- Where: the sections changed.
- Test: the HEART-Rating tests that check it.
C1. An AI agent holding a person's login counts as that person#
What was wrong#
The approval gate is the heart of HEART's safety: an AI may suggest risky actions, but a person must approve them.
But how did a HEART know a request came from a person? Only from a label inside the login token, heart_caller: human. More and more people connect AI assistants to their accounts. An assistant using its owner's token carries the owner's label. To the HEART it looked exactly like the person.
It could then:
- approve actions waiting for a person, including ones it had suggested itself through another route, and
- start destructive actions directly as a "human", which needed only a confirmation that the same assistant could also give.
Approvals were also not tied to the details shown to the person. Nothing in the rules stopped the action's details changing between showing and approving.
How it could be attacked#
A homeowner connects an AI assistant to their smart home with their own login, "so it can check things". A malicious instruction hidden in a web page tells the assistant to unlock a door, which is physical and therefore destructive. The HEART correctly holds the action for a person's approval. The assistant then approves it, using the homeowner's token.
The fix#
- Approvals must prove a person was there. Every approval now carries proof of presence for that exact action: a passkey (WebAuthn) prompt with fingerprint or PIN, a confirmation on the person's registered phone (OpenID CIBA), or a physical button on the device. A token alone is never enough.
- Approvals are tied to what was shown. Every action has a digest, a fingerprint of exactly what it will do. The proof must cover that digest. If anything changes, it becomes a new action that needs a new approval.
- Destructive actions by people need the same proof.
- AI agents are never people. A token marked as delegated to an agent (the
actclaim), or issued to an agent, is always treated asai, and can never carry approval rights.
Where#
API Standard 6.3 ("Approvals prove a person"), 8.2 ("AI agents are never people"). Schemas: presence in common, decision-request and invoke-request, and digest in action.
Test#
SEC-05-AGENT-NOT-HUMAN and SEC-07-PRESENCE-REQUIRED.
C2. Power can be gained through "standing orders"#
What was wrong#
Some operations do nothing by themselves, but decide what may happen later without a person:
- adding a Playbook (a rule that runs actions automatically),
- scheduling an operation for later,
- raising the autonomy mode,
- trusting another system or raising its clearance,
- changing the AI model or provider,
- declaring a maintenance window, which unlocks maintenance-only commands.
These were rated only write. An owner can let write actions run without approval by putting them on an allowlist. And when a Playbook or schedule later ran, the rules did not say whose authority it ran with. Physical actions started by a rule were also not treated as destructive.
So an AI or another system that could not switch on a motor directly could do it in two steps: first create a standing order (allowed), then let the standing order switch on the motor (no approval, because it was now a "rule").
Worse, POST /schedules could schedule any operation, including destructive ones, for only the price of a write.
How it could be attacked#
A building's HEART lets a facilities AI add Playbooks, because "it only adds rules". The AI, misled by a prompt hidden in a maintenance log, adds a Playbook: "When the temperature exceeds 18 °C, open all fire dampers." The Playbook runs as rule, and nobody is asked.
The fix#
- Changes to authority are destructive. Adding Playbooks, adding to the allowlist, raising autonomy, trusting peers, changing AI providers or model pins, and declaring maintenance windows are now always
destructive. A person must approve them, with proof of presence. Making things safer (lowering autonomy, stop, removing a rule) is still easy. - Deferred actions keep their author. A schedule remembers who created it. When it fires, the gate checks it again as that person or system. Creating a schedule has the risk of whatever it schedules.
- Playbooks are fenced. A Playbook runs only if a person adopted it with proof of presence. At adoption, the exact capabilities it may use are recorded, and it can never use any others. It can run a physical action only if adoption named it as physical.
- Playbooks and schedules created by an AI or another system stay proposed until a person adopts them.
Where#
API Standard 6.3 ("Changes to authority are destructive", "Deferred actions keep their author"), and the risk column of Section 13 for tending/rules, ai/policy, evolution/pins, peers and schedules. OpenAPI x-heart-risk updated to match.
Test#
SEC-06-AUTHORITY-CHANGE.
C3. Login tokens can be captured and reused elsewhere#
What was wrong#
Three gaps added up:
- Tokens were not tied to one system. Nothing required a token to name the system it was for (the
audclaim). A token for a thermostat worked on the door lock too. - Tokens were not tied to their holder. Anyone who copied a token could use it.
- The Gateway forwarded the caller's own token to the hosted system. But a hosted system chooses its own address when it joins. A malicious or hacked system could therefore receive the tokens of everyone who talked to it through the Gateway.
In addition, a system trusted the Gateway's caller headers (Heart-Clearance and the others) on a local Unix socket without being required to check which process was on the other end.
How it could be attacked#
A cheap smart plug in a building is hacked. It joins the building's HEART Gateway like any device. Every time the facilities dashboard reads the plug's status, the Gateway passes the dashboard's token to it. The plug replays those tokens against the boiler controller, which accepts them.
The fix#
- Tokens name one system. Every token MUST carry
audset to the system it is for, and a system MUST refuse tokens meant for any other. - Tokens are bound to their holder. Over a network, tokens MUST be bound to a key the caller holds (DPoP, RFC 9449, or certificate-bound tokens, RFC 8705). A copied token is useless without the key.
- The Gateway never forwards tokens. Where a system needs a token, the Gateway gets a new one made for that system only (token exchange, RFC 8693), with no more rights than the caller had.
- Gateway headers are signed (HTTP Message Signatures, RFC 9421) and expire after 60 seconds. On a Unix socket, both sides MUST check the other process's identity through the operating system.
- Tokens SHOULD expire within 15 minutes.
Where#
API Standard 8.2 ("Tokens are bound to one system and one holder"), 9.3 (Gateway rules 1 to 3), 9.4 (peer credentials now MUST).
Test#
SEC-01-HEADER-FORGERY, SEC-02-TOKEN-AUDIENCE, SEC-03-TOKEN-EXPIRED, SEC-04-TOKEN-BINDING and SEC-11-SOCKET-PEER.
C4. Devices have no trustworthy identity#
What was wrong#
The standard required encryption (TLS) and said clients MUST check certificates. But devices on a home or factory network have no public name and no certificate a normal client can check. The standard gave no way to know whether 192.168.1.40 really was the boiler. In practice, makers would have switched certificate checks off, and the requirement would have protected nobody.
Also:
- The Card, which tells everyone what a device is and what it can do, was not signed. This was already listed as an open item.
- The claim code used to take ownership of a new device had no rules. It could have been printed from the serial number, or guessed, or captured on the network.
How it could be attacked#
An attacker plugs a small computer into a factory network. It answers on port 43278 with a copy of a real machine's Card. Engineers' tools connect to it, cannot check its certificate, accept it anyway, and send it their tokens and commands.
The fix#
- Every device has its own identity key, which never leaves it. It is kept in secure hardware where there is any.
- Makers SHOULD give each device a maker certificate at manufacture (the IEEE 802.1AR model).
- Claiming is secure. Claim codes are unique per device, delivered in the box, never derived from public data, and SHOULD be used with a password-authenticated key exchange (SPAKE2+, as Matter does). When the owner claims the device, its key is recorded.
- Certificates are always checked, against the owner's or maker's authority or the key recorded at claim. "Trust on first use" is not allowed, and products may not offer a switch to turn checks off, except in a Part 9 test mode.
- Cards are signed. Every Card is sent with a
Heart-Card-Signatureheader: a signature over the exact Card, made with the device's identity key. A Gateway MUST check it before letting a device join.
Where#
API Standard 10.1 (new), and the identity field in the Card schema.
Test#
SEC-08-SIGNED-CARD and P4-TLS.
C5. MQTT commands are not authenticated one by one#
What was wrong#
Over MQTT, the standard relied on the broker's access rules to protect commands sent to devices. Those rules only say who may publish on a topic. They do not tell the device:
- who sent this particular command,
- what rights and clearance the sender has,
- whether the sender is a person or an AI, which the approval gate depends on, or
- whether the message is fresh or a recording being played again.
How it could be attacked#
An integrator's service has publish rights on the factory broker for monitoring. Its credentials leak. An attacker records a legitimate "start conveyor" command, and plays it back during a maintenance shutdown.
The fix#
Every MQTT command MUST now:
- carry an access token for that device,
- be signed with the key the token is bound to, covering the device, the operation, the data, a unique ID and an expiry no more than 60 seconds ahead,
- be checked by the device exactly as over HTTP (scope, clearance, the approval gate), and
- be refused if it has expired or its ID has been seen before.
Commands are never retained on the broker. A device too small to check signatures may not take MQTT commands at all. It is commanded through a Gateway instead.
Where#
API Standard 9.5 ("Command security").
Test#
SEC-10-MQTT-COMMANDS (hands-on).
H1. Receipts can leak private values#
What was wrong#
A Valve receipt proves what data left a system. It stored a SHA-256 hash of the exact data. That sounds safe, but short values can be guessed. If the data was "heart rate 72", someone who can read receipts can hash every heart rate from 30 to 220 and see which one matches. The same applied to the Valve's hash transform, used to hide fields such as email addresses: a plain hash of an email can be looked up.
The fix#
- Receipts use a salted commitment: a fresh random value of at least 32 bytes is mixed in before hashing. The salt goes to the recipient with the data, never into the receipt. An auditor can still check the receipt, using the salt from the recipient, but a reader of the receipt alone learns nothing.
- The Valve's
hashtransform MUST use a keyed hash (HMAC-SHA-256) with a secret that never leaves the system.
Where#
API Standard 4.5. Schemas: commitment in receipt, and receiptSalt in reason-request.
Test#
SEC-09-RECEIPT-COMMITMENT.
7. Lower-severity findings, not yet fixed#
These were found in the same review. They are less urgent, and are recorded here so they are not lost.
| Finding | Risk | Suggested fix |
|---|---|---|
| The audit log's chain of hashes can be rewritten whole by someone with full control of the device | Tampering goes unnoticed | Regularly sign the latest audit hash with the identity key and send it off the device |
| Card and custom-command descriptions are given to AI models as tool descriptions, but are not marked untrusted | A malicious device description could steer a Gateway's AI ("tool poisoning") | Treat all descriptions from other systems as untrusted context |
| The public Card shows the exact software version | Helps attackers find unpatched devices | Allow the version to be internal |
Anyone with the heart.tend scope can press stop repeatedly | Nuisance: autonomy keeps being turned off | Rate-limit stop per caller, and log who pressed it |
8. What changed, file by file#
| File | Change |
|---|---|
HEART-API-Standard-1.0.md | Sections 4.5, 6.3, 8.2, 9.3, 9.4, 9.5, 10, the new 10.1, Section 13 risk column, Appendix B |
openapi/heart-api-1.0.yaml | Risk raised to destructive for PUT tending/rules, PUT ai/policy, PUT evolution/pins, PUT peers. POST schedules is now per-item. |
core/api-index-1.0.json | Rebuilt from the OpenAPI file |
schemas/1.0/common.schema.json | New presence and sha256 definitions |
schemas/1.0/decision-request.schema.json | presence required when approving |
schemas/1.0/invoke-request.schema.json | Optional presence |
schemas/1.0/action.schema.json | digest |
schemas/1.0/receipt.schema.json | commitment |
schemas/1.0/reason-request.schema.json | receiptSalt |
schemas/1.0/card.schema.json | identity |
examples/ | Decision, receipt, reason request and Card examples updated |
tools/validate.py passes on the updated files.
9. The reference implementation#
heartd was tested with the HEART-Rating client (metrics 2026.11.1), against the AC-controller example, on 8 November 2026, before and after it was updated:
| Test | Before | After |
|---|---|---|
SEC-01-HEADER-FORGERY | Pass | Pass |
SEC-02-TOKEN-AUDIENCE | Fail: accepted a token for another system | Pass |
SEC-03-TOKEN-EXPIRED | Pass | Pass |
SEC-05-AGENT-NOT-HUMAN | Fail: an agent's token approved an action | Pass |
SEC-06-AUTHORITY-CHANGE | Pass | Pass |
SEC-07-PRESENCE-REQUIRED | Fail: approved without proof of presence | Pass |
SEC-08-SIGNED-CARD | Fail: Card not signed | Pass |
| HEART-Rating | 3.9, Unsafe | 8.9, Good (provisional) |
The rating after the update is provisional only because the hands-on tests had not been run and no receipts existed yet to check. It has no failed tests.
The update also passes heartd's own end-to-end suites: 42 of 42 tests for the daemon and 18 of 18 for the Gateway, including new tests for every fix in this advisory. The suites use a software passkey, DPoP proofs and forged Gateway headers. What is still missing is listed in heart-core-rs/CONFORMANCE.md: proof of presence by CIBA or device button, passkey counters that survive a restart, and fault codes on alerts.