---
title: 'Verify what a node runs: signed attestation in practice - LifeCare.ID®'
description: "What a LifeCare node publishes about what it runs: a measurement over its identity, code commit, loaded models and their licences, parameter version and twin recipe, signed with the node's own P-256 key, recomputable by anyone from the public /v1/attestation response, with the key checked against the keys LifeCare.ID® lists for the node. What a pass proves, what it does not, and how to check a node in your browser or from the command line."
canonical: https://lifecare.id/attestation
generated: 2026-10-08
---
Verify what a node runs: signed attestation in practice - LifeCare.ID®

[LifeCare.ID®](https://lifecare.id/)

Reference

# Verify what a node runs: signed attestation in practice

Consent decides who may touch a person's data. Attestation lets anyone check what the machine that holds it says it runs. This page describes the attestation a LifeCare node publishes, how to check it yourself, and the exact boundary of what a pass proves.

Published 4 September 2026, updated 30 September 2026 (the key check, and the wording that follows from it). Describes the node software serving the LifeCare network; the [verify page](https://lifecare.id/verify) is the tool this page explains.

## Why a node has to show what it runs

A LifeCare node holds a person's raw health data and runs the AI that reads it, on a machine owned and operated by the person's provider. The person controls the data through consent, but consent answers only half of the trust question. The other half is what the software on that machine does with the access it has: which code, which models, which learned parameters. A policy document cannot answer that. So every node publishes a claim about what it is running, bound to a measurement anyone can recompute and signed with the node's own key, at a public endpoint that needs no login. The measurement is also what a peer node reads before it accepts anything from another node.

## What a node attests

| Field | What it says |
|---|---|
| node_id, node_role | Which node, and its role in the network (a person's home node, later a data partner). |
| code_commit | The exact git commit of the node software that is running. Since the code is built from that commit, this is the claim "this is the program". |
| models | Every loaded model, as id, runtime, role and licence. Nothing more, so that the list is exactly what a verifier can see. |
| params_version | The content hash of the active learned-parameter set, the numbers the node's scoring has learned from experience. |
| twin_recipe_version | The version of the recipe by which the node would build a person's health twin. A constant reading "nothing is built yet" today; content-addressed once the twin layers ship, so a person can check the twin serving them was built by the recipe registered to them. |
| backend | How the measurement is rooted: software today, a hardware enclave quote when that backend is switched on. |
| measurement | The hash that binds the claim above. |
| signature, node_key | The node's ECDSA signature over the measurement, and the public key to check it with. |
| generated_at | When the quote was produced. Outside the measurement, so a quote can be refreshed without changing the claim. |

## The measurement, and the published-fields rule

The measurement is a SHA-256 over the claim core, serialised as canonical JSON: sorted keys, no incidental whitespace. It carries an `att-` prefix so it cannot be mistaken for anything else.

```
measurement = "att-" + sha256( canonical({
  "code_commit": "…",
  "models": [ { "id": "…", "license": "…", "role": "…", "runtime": "…" } ],
  "node_id": "…",
  "node_role": "…",
  "params_version": "…",
  "twin_recipe_version": "…"
}) )
```

One rule makes the measurement honest, and it cuts both ways. The hash may cover only what the response publishes, otherwise a verifier could not recompute it; so the node's internal model status, which carries observability fields such as whether a model is currently loaded in memory, is projected down to exactly the four published fields before hashing. And a claim the response publishes must be covered by the hash, otherwise anyone relaying the quote could rewrite that claim while the signature still verifies; that is why the twin recipe version sits inside the measurement rather than beside it. Adding it changed every node's measurement, which is correct: the node started attesting something new.

## The signature

Each node has its own P-256 signing key, generated on first run and kept in the node's state directory; only the public half leaves the machine, as a JWK in the attestation response. The node signs the measurement bytes with ECDSA over SHA-256. The same curve and encoding are used everywhere in the identity layer, so a node's signature and a person's passkey signature verify with the same code. Because the key is in the response, the first two checks need nothing else: recompute the measurement, then check the signature against the key. Those checks attribute the quote to a key, not to a machine, so a verifier also compares the key's id (its RFC 7638 thumbprint) with the keys LifeCare.ID® lists for the node at `/v1/node-keys`, as the node's operator declared them. A listed key, valid when the quote was made, reads "key known"; any other key reads "key unknown", and a revoked one fails. The section on limits below says what the list does not settle.

## A sample attestation

A real quote fetched from the first node on 4 September 2026; the measurement recomputes from these fields with the recipe below. The signature is shortened here.

```
GET /v1/attestation

{
  "node_id": "node0",
  "node_role": "home",
  "code_commit": "d58c34f",
  "models": [
    {"id": "qwen2.5:14b", "runtime": "ollama", "role": "reasoner", "license": "apache-2.0"},
    {"id": "qwen3-vl-30b", "runtime": "vllm@http://127.0.0.1:8090", "role": "companion", "license": "apache-2.0"}
  ],
  "params_version": "params-057434acaa325a2c",
  "twin_recipe_version": "twin-0",
  "generated_at": "2026-09-04T16:08:38Z",
  "backend": "software",
  "measurement": "att-c5a4acdfe2b70a3c816d161b11197dc03835c6e4b49a135569a856632c196c56",
  "signature": "MEUCIEJZQiG8CmkOoX4wuO6LVolP1BeSit3jqO3_DY4J\u2026",
  "node_key": {"kty": "EC", "crv": "P-256", "x": "5MxPh5WMBFRmkAnwlqzcl-0QNvpazJnAr1z_EH0ffn4", "y": "ORw9b7qA1oTQR3mHOIS2eIKPm8MZV18SXykAWLvza-o"}
}
```

No health data, no secrets. The endpoint is unauthenticated on purpose: the point is that anyone can read it, including a person who is not a member and a peer node that has never seen this one before.

## Verifying it yourself

**In your browser.** The [verify page](https://lifecare.id/verify) fetches a node's attestation from the address you give it, recomputes the measurement from the response, compares it to the published one, checks the signature against the key the response carries using the browser's own cryptography, and looks that key up in the list of node keys LifeCare.ID® publishes, all on your machine. It reads "Verified, key known" only when all three pass, "Signature valid, key unknown" when the signature is valid but the key is not listed for that node, and "Not verified" otherwise, and it has a "simulate tampering" switch so you can watch a single changed character fail the check. You can also paste an attestation you obtained some other way.

**From the command line.** The recomputation is short enough to read, and running it on the quote above prints `True`:

```
curl -s https://your-node.example/v1/attestation | python3 -c '
import sys, json, hashlib
a = json.load(sys.stdin)
core = {k: a[k] for k in ("node_id", "node_role", "code_commit", "params_version", "twin_recipe_version")}
core["models"] = [{k: m.get(k) for k in ("id", "runtime", "role", "license")} for m in a["models"]]
recomputed = "att-" + hashlib.sha256(json.dumps(core, sort_keys=True, separators=(",", ":")).encode()).hexdigest()
print("measurement recomputes:", recomputed == a["measurement"])'
```

The signature check needs an ECDSA library; the verify page's source shows the exact steps, including the conversion between the DER encoding the node emits and the raw form browser cryptography expects.

## What a pass proves, and what it does not

A pass proves two things. The claim is internally consistent: the measurement you recomputed from the published fields is the one the node signed, so nothing in the claim was altered in transit or by whoever relayed it. And the claim is attributable: it was signed by the holder of the private key matching the published public key. With the key known, that key is the one the node's operator declared for this node, so a quote signed with any other key does not read as this node's.

A pass does not prove that the machine is running what it claims. This is the software floor: the node attests what it reports about itself, and an operator who controls the machine could report a consistent lie. Two steps close that gap, and both are designed in.

- **Anchoring the key to an identity.** Today the key list is the operator's declaration, published by LifeCare.ID®. The next step publishes the key in the node's own decentralized identifier under the network's domain, in the `:node:` namespace the identity layer already reserves, so a verifier resolves the key from the node's identity document, and a rotation is an update to that document rather than an edit to a list. Then a quote is attributable to a node identity anyone can resolve, not to a list. Vouching for who operates a node is a further step.

- **Hardware-rooted measurement.** The attestation interface has a `backend` field for a reason. On hardware that can attest in silicon, NVIDIA confidential computing on server-class GPUs and Apple Private Cloud Compute-class enclaves at the other end of the node hardware range, the measurement becomes a quote rooted in the platform's own key: unforgeable and independent of the operator. Those backends implement the same interface and replace the software floor without touching anything that reads it. They are gated to the first deployment where they matter, the second node and the first clinic. The machine that serves the network today offers no confidential computing, so its attestation is the software floor.

Until then, the value of the software floor is transparency and integrity: any change to the code, the models, the parameters or the twin recipe changes the measurement, the change is visible to everyone, and the claim is signed. That is more than a policy and less than a proof, and the page says exactly which.

## Why the licences are inside the measurement

A model's licence is part of what a node attests because the freedom to run the model anywhere is part of the sovereignty claim. A node whose models are permissively licensed can be rebuilt by another operator, on other hardware, from the same manifests; a node depending on a model it may not redistribute cannot. Putting the licence in the measured claim makes that posture something a verifier sees, rather than something they are told.

## What else the node signs

The same key signs the node's learned-parameter sets, so the parameters a person's scoring runs on are attributable, and it will sign the derived records a node shares with peers when replication lands, so a peer verifies before accepting. It also signs the node's requests to the identity layer when it records what it holds about a person, so the resource registry knows which holder wrote each line. One identity, every artifact the node publishes.

## Further reading

- [Verify a node](https://lifecare.id/verify): the tool.

- [Apple Private Cloud Compute](https://security.apple.com/blog/private-cloud-compute/) and its [security research environment](https://security.apple.com/blog/pcc-security-research/): the transparency-log and hardware-rooted model this design emulates and will adopt on capable hardware.

- [W3C Decentralized Identifiers](https://www.w3.org/TR/did-core/): the anchoring step for the node's key.

- [Zero-egress health AI](https://lifecare.id/zero-egress): the property the attestation lets you check, and [the consent receipt](https://lifecare.id/consent-receipt) and [the patient-held authorization server](https://lifecare.id/authorization-server): the other half of the trust model.

- [The paper: design and early operation](https://lifecare.id/paper), the preprint that puts the receipt, the tokens, the enforcement and the attestation in one document, with the design rationale and the limits.

**A note on the two halves.** Consent and attestation answer different questions and neither substitutes for the other. A perfect consent ledger over a node running unknown software is a record of permissions given to a black box; a perfectly attested node with no consent layer is a transparent machine nobody authorised. The network runs both, and each is checkable by the person and by anyone they ask to check for them.
