LifeCare.ID® Developer Docs Public

Trust model ✅

Four mechanisms carry every trust claim LifeCare.ID® makes, and each one has a canonical reference page on lifecare.id that states it against the implementation serving the network today, over a published, drift-tested contract. Those pages are the source of truth and they move when the code moves. This page is the developer's frame around them: what each mechanism means while you build, which of your code paths it touches, and where the honest limit sits.

Nothing here is a second copy of a canonical page. Where you need the field table, the hash recipe or the sample payload, follow the link.

Mechanism What it answers Canonical page
Consent receipt who permitted what, for which purpose, until when The consent receipt ↗
Patient-held authorization server may this specific request be served, right now The patient-held authorization server ↗
Zero egress where the raw values are allowed to be while a model reads them Zero-egress health AI ↗
Node attestation what the machine holding the data says it runs, and whose key signed that statement Signed attestation ↗ · Verify a node ↗

The two halves. Consent decides who may touch the data; attestation lets you check what a node says it runs. Neither substitutes for the other, and the network runs both.


The ceiling, stated first

The attestation limit is the one you would otherwise find on your own in thirty seconds, so it goes at the top. From the verify page ↗, verbatim:

Three checks run here. The measurement recomputes from the response, so nothing in the claim is hidden. The signature over it is valid for the key the response carries, so the claim was not altered after that key signed it. And the key's id (its RFC 7638 thumbprint) is compared with the keys LifeCare.ID® lists for the node, as published by its operator: Verified, key known needs all three. A valid signature by a key that is not listed reads Signature valid, key unknown, because the quote may come from a different machine. This is the software floor: it is not yet hardware-rooted, so an operator in control of the machine could still publish a consistent claim that is not true; hardware-rooted attestation closes that gap and is designed in. The key list is what the node's operator declared; anchoring each key in the node's own decentralized identifier is the next step.

And from the attestation page ↗:

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.

Design accordingly. A verified attestation is a strong integrity and transparency signal (any change to code, models, parameters or twin recipe changes the measurement, visibly and under signature), and it is not yet a hardware proof. For a high-assurance flow, treat hardware-rooted attestation 🛣️ as the bar and say so in your own security documentation rather than borrowing more assurance than the mechanism gives.


Every grant produces two artifacts from one event: a plain-language ledger line for the person, and a machine-readable receipt shaped to ISO/IEC TS 27560:2023 for you, for the resource holder, and for anyone auditing either.

What you see. POST /v1/consent/grant returns the receipt inline. Its fields are consent_id, schema, subject_did, grantee, data_categories, purpose (a dpv term and the text the person read), granted_at, expires_at, revoked, revoked_at, proof, and revocation_endpoint. Categories, never values: a full copy of every receipt ever issued names who trusted whom for what, and not one measurement.

What that means in your code:

Honest limit. A W3C Verifiable Credential wrapping of the receipt is on the roadmap 🛣️ and is not what ships today.

Build detail (endpoints, the canonical grant payload you hash for a passkey challenge, listing grants, the ledger): Scopes & consent. Mechanism, shape and standards lineage: the consent receipt ↗.


2. Authorization server: the failure modes you must implement

The authorization server issues tokens against grants; the resource server holds the data and honours them. They are different parties by design, and the policy the authorization server applies is the person's own consent.

What that means in your code. A resource server never decodes a token. It introspects and branches on token_type, failing closed on any shape it does not implement. The four shapes and their exact fields are in Authorization & tokens; here is how they behave from the client side.

What you see What actually happened What to do
active: false expired, token revoked, its grant revoked, or unknown. Four causes, one answer, so a caller learns nothing from the difference stop using the data, degrade the surface, re-consent if the person wants it back. Do not retry-storm: this is a normal state
403 on POST /v1/token no active grant covering that scope with a matching purpose that you may use. A grant to another party counts as none: a token is issued only against a grant naming you as grantee, with one exception for the deployment's own first party (Authorization & tokens) ask for consent. Do not retry with a different purpose string hoping for a hit: purpose is part of the lookup, not a check after it
token_type: "user" vs "access" a signed-in person versus an app or agent holding a grant-backed token user-self actions (granting or revoking that person's own consent) require a user token whose subject matches. An access token presented for the same action is refused with 403
token_type: "delegated" an actor exercising a subset of a person's authority published so you can write the branch now. No endpoint mints one yet

Three properties worth designing around rather than discovering:

Honest limits. Caller authentication and the device proof on grants are implemented and verified whenever presented, but whether an unauthenticated call is refused is a deployment setting (see the rollout sequence in Authorization & tokens). Binding a token to its grantee rides on the same setting: with caller authentication off there is no proven identity to bind to. The delegated shape is in the published contract and no code path issues it yet.

Mechanism, the UMA 2.0 lineage and what was deliberately collapsed: the patient-held authorization server ↗.


3. Zero egress: what it means for a data-source integration

Zero egress means the models run where the raw health data lives, and what leaves is interpretation: a score, a band, a sentence with no number in it. It is enforced on three code paths, not promised in a policy: where models may run (loopback endpoints and declared model ids only), what an agent may read (a guarded accessor limited to declared scopes, every read token-backed and ledgered), and what may leave (a complete field whitelist per response, prose screened numeric-free).

What that means if you are building a data source or connector:

What zero egress does not mean. Not air-gapped: a node fetches its sources, serves interpreted outputs to the person's app, and talks to the authorization server about tokens, receipts and the registry. It is a statement about raw values, not about network traffic.

Honest limit, from the canonical page: the shared model server on the current node also lists a catalog entry that proxies to a cloud model, used by operator tooling. It sits outside the allowlist, so no agent can reach it, and moving it to a separate server so the allowlist becomes defence in depth is scheduled work.

Your obligations as an integrator: Security & compliance and Data sources & connectors. Mechanism and the three gates: zero-egress health AI ↗.


4. Attestation: verifying a node before you trust it

Every node publishes a signed claim about what it runs at GET {node}/v1/attestation, 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.

Verify it programmatically, in five steps:

  1. Fetch GET {node}/v1/attestation. No credential, no client registration.
  2. Recompute the measurement over the published claim core as canonical JSON (sorted keys, compact separators), with the att- prefix. Only published fields are hashed, which is what makes recomputation possible; and every published claim is covered, which is what stops a relay from rewriting one. The exact recipe and a runnable snippet are on the canonical page.
  3. Check the signature. ECDSA over SHA-256 with the node's P-256 key, whose public half ships in the response as a JWK. Browser cryptography wants the raw r||s form, so convert from the DER encoding the node emits.
  4. Check whose key it is. Compute the key's RFC 7638 thumbprint (SHA-256 over {"crv","kty","x","y"}, sorted, no whitespace, base64url without padding) and look it up in GET https://lifecare.id/v1/node-keys, the keys each node's operator has declared. Listed for the quote's node_id as a current key, or as a retired key with the quote's generated_at before its until, is key known. A revoked key fails outright, whatever date the quote carries: generated_at is the quote's own statement, outside the signature, and whoever holds a key can write any date on what it signs, so revoked_from is shown as information and opens no window. Anything else is key unknown. The zero-dependency library does this for you (classifyKey, and fetchAndVerify with a registryUrl).
  5. Gate on the contents, not just the pass. code_commit, the models list with their licences, params_version and twin_recipe_version are the claim. A pass tells you the claim is intact and signed; you still decide whether that commit and those models are ones you will integrate with.

Do not skip step 4 into misplaced confidence. See the ceiling above. A pass with the key known proves internal consistency and that the key is the one the node's operator declared for it. It does not prove the machine runs what it claims, and the key list is the operator's declaration until node DIDs land 🛣️.

For your users, the browser verifier at lifecare.id/verify ↗ runs the same checks locally, with a "simulate tampering" switch that shows a single changed character failing. Pass the node address (?node=<url>) for a one-click link. The badge recipe is in Recipes; the endpoint and the boundary are in Security & compliance, under "Node transparency".

Mechanism, the published-fields rule, and why model licences sit inside the measurement: signed attestation ↗.


Why these pages, and not this one, are canonical

The four reference pages are maintained against the code that serves the network, republished with the site, and checked after every deploy; the API surface behind them is a published, drift-tested contract (openapi.json). A second description maintained by hand would drift from the first, and a trust document that has quietly drifted is worse than none. So this page frames and links, the canonical pages state, and the contract decides.

If a claim here and a claim there ever disagree, the canonical page wins and the disagreement is a bug. Report it through the channel your onboarding gave you.