A triage agent reads an inbound referral, decides it needs the patient's prior imaging reports, and asks a retrieval agent to fetch them. The retrieval agent calls the FHIR server; the server authorises the call, returns the documents, and writes an audit entry naming the retrieval service. Every component behaved correctly and every control fired as designed. The companion teardown to this piece — The triage agent asked the retrieval agent, and the record shows neither asked anyone — walks the evidence that what is left behind cannot answer the question a privacy officer, a breach analyst or opposing counsel will actually ask: on whose authority was that record opened, and was the authority narrower than the one the clinician held. I am going to take that as established and design against it.

Two records over one clinical run One run. One set of boxes. Two different assertions — and your audit log makes only the first of them. THE RECORD YOU HAVE — ACTIVITY, IN THE ORDER IT HAPPENED Clinician signs the order Triage agent reads the referral Retrieval agent fetches prior reports FHIR server returns the documents launch delegates HTTP GET What the audit entry holds One flat user identity — the retrieval service — plus a timestamp, the patient, and the resource that was read. No field in it names the principal on whose authority the read was performed. No provision requires one. THE RECORD THE QUESTION NEEDS — AUTHORITY, AND WHERE IT CAME FROM Ordering clinician root grant · a named human, not a service The Medicare conditions of participation already name this person: the practitioner who authenticates the order. The root grant is minted at that act, not at login. attenuate Triage agent grant 2 — child of 1 keep: read on Encounter and DocumentReference, one patient only. caveats: expires in 15 minutes · purpose of use recorded as treatment. attenuate Retrieval agent grant 3 — child of 2 keep: read on DocumentReference alone. Nothing else survives the intersection. caveats accumulate: 5 minutes, one patient, audience is this FHIR server. present FHIR server verifies the chain offline Walks the chain from the root and recomputes the running intersection itself. It never trusts what the last credential claims about its own authority. The upper record answers who acted — which is what the audit-control standard asks for, and it is satisfied. The lower record answers on whose authority — which is what a breach analysis, a disclosure accounting and a deposition ask.

This piece is the healthcare instantiation of a primitive specified generally elsewhere: authority carried rather than ambient, narrowed at every hop by a constructor that cannot widen, recorded as a graph rather than a list. The general design and its reference implementation are set out in Carrying authority across handoffs, and I will not repeat that derivation. What I will do is show that in this sector the design is not a preference. Almost every property it has is already required of you in prose, by rules written for humans and paper; the only thing missing is the runtime object.

The rules name every piece except the one that matters

Start with what US health privacy law actually demands of an identity, because it is less than people assume. The Security Rule's technical safeguards require, at 45 CFR 164.312(a)(2)(i), that a covered entity or business associate "[a]ssign a unique name and/or number for identifying and tracking user identity." That is the whole of it. One flat object per user. There is no provision — and I mean this precisely, having read the section rather than a summary of it — requiring anyone to record the principal on whose authority that identity acted. It is worth noting how settled this text is: the source citation on 164.312 reads [68 FR 8376, Feb. 20, 2003, as amended at 78 FR 5694, Jan. 25, 2013], so the technical safeguards have not been amended since the Omnibus Rule of January 2013, which is to say since before the transformer paper.

The audit obligation has the same shape. Section 164.312(b) requires entities to "[i]mplement hardware, software, and/or procedural mechanisms that record and examine activity in information systems that contain or use electronic protected health information." Activity. What happened, in the order it happened — the call graph, in the vocabulary of the companion piece. Nothing in the standard reaches the grant under which the activity was performed. On the administrative side the framing is consistent: 164.308(a)(4)(ii)(C) asks for policies that "establish, document, review, and modify a user's right of access to a workstation, transaction, program, or process," and 164.308(a)(1)(ii)(D) asks for procedures "to regularly review records of information system activity, such as audit logs, access reports, and security incident tracking reports." Access is a property of a user. Review is a review of activity. Delegation is not an object in this scheme at all.

That reads like a gap in the law, and it is tempting to write the essay about regulation not catching up. That essay would be wrong. The law has the concept, in four places. It has it everywhere except in the runtime.

Minimum necessary is already per-class and per-category, at the point of use. 45 CFR 164.514(d)(2)(i) requires a covered entity to identify "those persons or classes of persons, as appropriate, in its workforce who need access to protected health information to carry out their duties" and, "[f]or each such person or class of persons, the category or categories of protected health information to which access is needed" — and then, under (d)(2)(ii), to make reasonable efforts to limit access consistent with those categories. Read that as an engineer rather than as a lawyer and it is a specification for an authority set: a mapping from a role to a bounded collection of data categories, applied where the data is used. An agent that inherits a clinician's session credential is, by construction, not limited to any identified category. It holds the union of everything that session could reach. There is no honest way to describe that as consistent with (d)(2)(ii), and no field anywhere in the resulting audit entry in which the category it was actually limited to could have been written down.

Monotone attenuation is already mandatory — at the legal layer, and only there. 45 CFR 164.502(e)(1)(ii) provides that a business associate may allow a subcontractor to create, receive, maintain or transmit protected health information on its behalf only "if the business associate obtains satisfactory assurances that the subcontractor will appropriately safeguard the information" — the same obligation a covered entity owes its business associate under (e)(1)(i). That is a chain in which each hop must carry a named, no-stronger grant from its parent, and it is enforced through a stack of contracts. It is the exact structure this design puts in the credential. The law already believes delegation must be attenuated and attributable; it simply expects the attenuation to live in a filing cabinet, because when the rule was written the delegating parties were organisations and the delegation took weeks. When the delegating party is a model choosing a sub-agent inside a two-second turn, a contract is not a mechanism.

The authority check is already specified as being at the operation, not at the door. FDA's electronic-records rule names the control the multi-agent gap breaks, in one clause. 21 CFR 11.10(g) requires "[u]se of authority checks to ensure that only authorized individuals can use the system, electronically sign a record, access the operation or computer system input or output device, alter a record, or perform the operation at hand." The operation at hand. Not the session, not the login, not the token minted at the front door and passed inwards. And 11.300(a) makes the identity assumption explicit: controls must ensure "no two individuals have the same combination of identification code and password." A system in which four agents act under one clinician's credential does not violate the letter of 11.300(a), because there is only one identification code and one individual behind it. It comprehensively violates the assumption the clause was written on top of — that an identification code and an actor are the same thing.

And in one corner of federal law, the grant already physically travels with the data. 42 CFR part 2, which governs substance-use-disorder treatment records, is the closest thing in US law to a carried credential. Section 2.32 requires that each disclosure made with the patient’s written consent be accompanied by a notice stating "This record which has been disclosed to you is protected by Federal confidentiality rules (42 CFR part 2)," restricting the recipient's onward use, and warning that "[a] general authorization for the release of medical or other information is NOT sufficient"; 2.32(b) further requires the recipient to receive a copy of the consent or a clear explanation of its scope. Strip the paper and what is left is a rule that a disclosure must arrive with its grant attached, that the grant must be specific rather than general, and that the recipient's authority is bounded by it. That is a macaroon with a fax machine.

Against those four, look at what the available standards provide. Certified health IT must log privilege changes: 45 CFR 170.210(e)(1)(i) requires that "[t]he audit log must record the information specified in sections 7.1.1 and 7.1.2 and 7.1.6 through 7.1.9 of the standard specified in § 170.210(h) and changes to user privileges when health IT is in use," and 170.210(h) specifies "ASTM E2147-18, (incorporated by reference in § 170.299)." The criteria at 45 CFR 170.315(d)(2) and (d)(10) require that recorded actions "must not be capable of being changed, overwritten, or deleted by the technology" and that the technology "be able to detect whether the audit log has been altered." Those are exactly the integrity properties an authority record needs, already law for the log sitting next to it.

I cannot tell you what is in sections 7.1.1 through 7.1.9 of ASTM E2147-18, and neither can you without paying for it. The standard is incorporated by reference into the CFR, which makes its contents legally operative for every certified electronic health record in the country, and ASTM sells it. So I can name which sections are mandated and will not paraphrase them. The operative field list for every certified EHR audit log in the United States sits behind a paywall — the public cannot check a vendor's log against the rule without first buying the rule.

The API standard is more revealing, because it is the one the agents will speak. The version adopted at 45 CFR 170.215(c), and so the one the standardized-API certification criterion at 45 CFR 170.315(g)(10) is tested against, is SMART App Launch Release 2.0.0; the passages quoted here read identically in the current STU 2.2. It defines scopes of the form [patient|user|system]/ResourceType.[cruds] and states that "[s]ystem-level scopes describe data that a client system is directly authorized to access; these scopes are useful in cases where there is no user in the loop, such as a data monitoring or reporting service." An explicit no-human-in-the-loop credential class, which is honest and necessary. What the specification does not contain is any mechanism for one client to delegate a subset of its granted scopes to another, or a field recording which principal authorized a token. The vocabulary for narrowing exists. The vocabulary for narrowing on behalf of someone does not.

The sector's own audit-record standard is the same. In HL7 FHIR R5, AuditEvent.agent is "[a]n actor taking an active role in the event or activity that is logged," with agent.who required at one and only one, and agent.requestor defined as an "[i]ndicator that the user is or is not the requestor, or initiator, for the event being audited." You may list several agents on one event; you may not express a relationship between them. There is no delegation element, no on-behalf-of link, no authorizer-versus-performer edge. A boolean is not an authority chain — it is a flag on a flat list, which is the shape 164.312(a)(2)(i) asked for and no more.

Be careful how that reads. Across 45 CFR 164.312, 164.308, 170.210(e), 170.315(d)(2) and (d)(10), 21 CFR 11.10, FHIR R5 AuditEvent and SMART App Launch (Release 2.0.0 and STU 2.2), I could locate no requirement that an authority chain, a delegation record or an on-behalf-of relationship be captured. The correct statement of that is that no provision requires it — not that anything prohibits it. Nobody declined to specify this. These rules govern systems built by and for people, and a person does not hand their authority to another person forty times a minute.

Why the record has to outlive everyone who made it

Most sectors' version of this argument ends at the audit log. Healthcare's does not, because the retention horizon is set by regulation rather than by storage budgets, and the periods stack. HIPAA documentation must be retained "for 6 years from the date of its creation or the date when it last was in effect, whichever is later" under 45 CFR 164.316(b)(2)(i). A Medicare-participating hospital must keep medical records "in their original or legally reproduced form for a period of at least 5 years" under 42 CFR 482.24(b)(1). A clinical investigator must retain records "for a period of 2 years following the date a marketing application is approved for the drug for the indication for which it is being investigated; or, if no application is to be filed or if the application is not approved for such indication, until 2 years after the investigation is discontinued and FDA is notified" under 21 CFR 312.62(c) — a clock that starts when a programme ends. And under Part 11 the audit trail "shall be retained for a period at least as long as that required for the subject electronic records," so it inherits whichever clock is longest.

Three readers, and none of them is a dashboard.

  1. The breach analyst, working against a presumption. 45 CFR 164.402 provides that an impermissible acquisition, access, use or disclosure "is presumed to be a breach unless the covered entity or business associate, as applicable, demonstrates that there is a low probability that the protected health information has been compromised based on a risk assessment" whose factors include "(ii) The unauthorized person who used the protected health information or to whom the disclosure was made" and "(iii) Whether the protected health information was actually acquired or viewed." If the authority graph is unrecoverable, factor (ii) has no answer, and the presumption stands. And 164.414(b) puts the burden where it hurts: "the covered entity or business associate, as applicable, shall have the burden of demonstrating that all notifications were made as required by this subpart or that the use or disclosure did not constitute a breach." Absence of evidence is not neutral here. It is a loss.
  2. The patient, six years deep. 45 CFR 164.528(a)(1) gives an individual "a right to receive an accounting of disclosures of protected health information made by a covered entity in the six years prior to the date on which the accounting is requested," and (b)(2) requires each entry to state the date, "the name of the entity or person who received the protected health information," a description of the information, and "a brief statement of the purpose of the disclosure that reasonably informs the individual of the basis for the disclosure." Naming the recipient and stating the purpose is an authority-graph query. It is not derivable from a list of service calls.
  3. Opposing counsel, with a copy and no goodwill. Since 1 December 2017, Federal Rule of Evidence 902(13) has made self-authenticating "[a] record generated by an electronic process or system that produces an accurate result, as shown by a certification of a qualified person that complies with the certification requirements of Rule 902(11) or (12)," and 902(14) does the same for "[d]ata copied from an electronic device, storage medium, or file, if authenticated by a process of digital identification." The practical effect is that your log walks into court on a certificate rather than on a witness. The record is the testimony. Whatever it says is what you said.

One more deadline turns this from an evidentiary problem into an operational one. 45 CFR 164.410(b) gives a business associate "without unreasonable delay and in no case later than 60 calendar days after discovery of a breach," and 164.410(c)(1) requires the notification to include "identification of each individual whose unsecured protected health information has been, or is reasonably believed by the business associate to have been, accessed, acquired, used, or disclosed during the breach." Each individual, in sixty days. From a call graph that lost the authority chain, that is a forensics project with a statutory deadline attached, and the fallback when it fails is to notify everyone who might have been affected.

That is the constraint the design has to satisfy, and it is sharper than the one a platform faces. It is not enough that the authority chain be verifiable offline. It has to be verifiable in eight years, by someone with a copy of the record, no access to your running estate, no relationship with you, and a professional interest in the answer being no.

Four premises, in a clinical register

Everything that follows rests on four statements. I keep them separate from the design so a reader who rejects one can say which, rather than arguing with the consequences.

One: authority is carried, never ambient. No component acts under authority it happens to possess. The patterns I have read do the opposite, and I have no measurement of how far they dominate — a sub-agent inherits the parent's HTTP client, a tool call runs under the session's token because that is what the session had, a background worker holds a service account that can read every chart in the estate because at some point it needed to read one. In each case the authority is a property of where the code is running rather than of what it was asked to do, and nothing in the request identifies the reason the request was permitted.

Two: every delegation mints a strictly weaker credential naming its parent, and the root names a person. This is where the clinical instantiation departs from the general one. In a platform, the root of the chain can legitimately be a workload. Here it cannot. The root grant's subject must be the ordering clinician, because 42 CFR 482.24(c)(1) requires that "[a]ll patient medical record entries must be legible, complete, dated, timed, and authenticated in written or electronic form by the person responsible for providing or evaluating the service provided," and (c)(2) requires all orders, including verbal orders, to be "dated, timed, and authenticated promptly by the ordering practitioner or by another practitioner who is responsible for the care of the patient." An agent-written entry must resolve to one of those named practitioners or the condition of participation is not met. Attenuation is what preserves that resolution through an arbitrary number of hops: because every child names its parent and no child exceeds it, the chain terminates in the human who signed, however deep the machinery goes.

Three: the chain is verifiable offline, by a stranger, years later. A verifier that must call an issuer has coupled every clinical request to the availability of a central service, which is unacceptable on a path where the failure mode is a delayed diagnosis. A verifier that must reconstruct the run from telemetry has made a control decision depend on a sampled, best-effort stream. But the healthcare version goes further than either: because of the retention horizon and Rule 902(13), the verification must also be performable by a party with the record and nothing else. That forces things the platform version does not need — an archived key, a frozen serialisation format with a version number, and a written procedure a qualified person can certify.

Four: attenuation is monotone, which is minimum necessary made structural. A child can never hold more than its parent. Not as a policy a reviewer checks, and not as a rule in a document — as a property of the only constructor that exists. The child does not request authority; it names which of the parent's authority to keep, and the constructor computes the intersection. This converts the minimum-necessary duty at 164.514(d)(2) from something a workforce member is trained to observe into something the type system cannot violate, and it is the premise that does the most work.

The meet, not the request Minimum necessary stops being a policy somebody applies and becomes an intersection somebody computes. THE PARENT GRANT read · Encounter/patient-P read · DocumentReference/patient-P read · Observation/patient-P write · ServiceRequest/patient-P caveats: patient-P · expires 15 min purpose of use: treatment Held by the clinician who signed the order. Nothing below can exceed it. WHAT THE CHILD ASKS TO KEEP read · DocumentReference/patient-P read · Observation/patient-P read · MedicationRequest/patient-P not held by the parent read · DocumentReference/patient-Q a different patient — outside the parent's scope add caveat: expires 5 min add caveat: audience is this server THE CHILD THAT RESULTS read · DocumentReference/patient-P read · Observation/patient-P caveats: patient-P · treatment · expires 5 min · audience bound parent: grant 1 seal: keyed by the parent's seal Strictly weaker. Names its parent. Checkable without asking anyone. The child never asks for a permission. It asks which of the parent's permissions to carry forward, and the constructor computes the intersection. Anything it names that the parent did not hold is absent, not refused — and no function anywhere in the package returns a result larger than its input. WHAT SHIPS TODAY INSTEAD The child inherits the parent's session credential, unchanged. Its authority is identical to the parent's — every resource type, every patient the session could reach. The category of information it actually needed appears nowhere in the record, so an agent inheriting a session credential is by construction not limited to any identified category — which is what the minimum-necessary rule asks.

The strongest objection to all of it is clinical rather than technical, and it deserves stating at full strength before I answer it.

You are proposing to insert a new failure mode into a path where failure means a clinician does not see a prior scan. Agent workflows decide their own shape at runtime; you will not know what the retrieval step needs until it needs it. Whatever narrowing you write will be either so generous that it is decorative, or so tight that the workflow fails on the first legitimate thing it was not anticipated to do — and at three in the morning, with a patient waiting, somebody will widen the root grant until the failures stop and nobody will ever narrow it again.

That objection is correct about the failure mode and I have no clean answer to it. What I have is a claim about where the failure becomes visible. In an ambient-authority system the person who widens the grant produces no artefact: the workflow stops failing, and the widening is discoverable only by reading the deployment history of a config file. In a carried-authority system the widening is a root grant with a broader authority set, signed, timestamped, attributed to a named person, and sitting at the head of every chain issued afterwards. It does not prevent the three-in-the-morning decision, and it should not — clinical urgency is real and break-glass is a design requirement rather than a bug. It makes the decision a thing rather than an absence, and in a sector where the burden of proof runs against you, the difference between a thing and an absence is most of what governance is.

The second objection is about measurement, and I concede it outright. It would be useful to know how attribution accuracy degrades with agent-to-agent handoff count — whether the practical break comes at the second hop or the ninth. I could locate no primary published measurement of it: no benchmark, no regulator study, no vendor telemetry publication. Any circulating figure for the share of agent actions attributable to an authorising principal should be treated as unsourced until a primary publication turns up. I am designing against a gap whose magnitude nobody has measured, which is a weaker position than I would like and the honest one.

What the premises force

Take them in order and ask what each rules out.

Carried, not ambient rules out a sub-agent sharing a process, an environment or an HTTP client with its parent. The credential must be a value passed as an argument, which means it must fit in a header — a size budget that returns later as a real cost.

The root names a person rules out minting the root grant at session login, which is where an implementation will want to put it because that is where the identity provider already sits. It has to be minted at an act of clinical authorisation instead — an integration into the clinical application rather than into the identity layer, and the hardest piece of this to land, because it touches a workflow clinicians already find slow.

Verifiable offline by a stranger rules out anything whose authoritative answer lives in a running service: no introspection endpoint on the hot path, no policy round trip, no lookup of the parent grant to confirm it existed. It also rules out something subtler — an implicit dependency on your own code. If verification requires running your library, the party checking the record in year eight depends on a build artefact that may not exist. The serialisation has to be specified and versioned as a document, so a competent engineer could write a second implementation from the description and get the same answer. That is what makes a certification under Rule 902(13) meaningful rather than a formality.

Monotone rules out the design almost everyone reaches for first: a request-and-issue flow where the child says what it wants and something decides. That produces a child holding more than its parent whenever the deciding component is wrong, misconfigured or persuaded — and here the deciding component is a model in a tool loop.

The data model

A grant is the only thing that travels. It carries an authority set, a set of caveats, a pointer to its parent, and a seal that binds it to the chain above it. What the clinical instantiation adds is three things the general form leaves to the caller.

  1. A permission grammar shaped like the minimum-necessary catalogue. Not a verb and a URL, but a verb, a FHIR resource type, and a subject scope — because 164.514(d)(2)(i) asks for categories of information per class of person, and a permission that cannot express "document references for this patient only" cannot express the duty.
  2. Purpose of use as a first-class caveat. 164.528(b)(2) requires the accounting entry to carry "a brief statement of the purpose of the disclosure." If purpose is not in the credential at the moment of the call, it is being reconstructed afterwards from context, and a reconstructed purpose is exactly the kind of assertion that does not survive cross-examination.
  3. A root grant whose subject is a person, enforced by the type. The root-issuance function accepts a clinician principal and nothing else. A service account cannot be a root. This is one line of type-level discipline that removes the most likely way this design would be defeated in practice — by wiring the root to whatever identity the batch job happened to run under.

One property is easy to miss and is load-bearing: the verifier recomputes the authority rather than reading it. The authority field on a grant is a convenience for the holder and for the record; the verifier walks the chain from the root and recomputes the running intersection itself, so a grant whose stated authority disagrees with what the chain actually permits is evaluated on the chain. Without that, the whole invariant rests on every minting party being honest, and one of the minting parties is running inside a model's tool loop.

Configuration

Attributable delegation, instantiated for a clinical estate

Four files, written against a parameterised form of the reference implementation — Grant, attenuate and verifyChain taken over a permission type and a caveat type, plus two fields the clinical case needs and the general one does not: a display name for the record, and a root-minting entry point. They will not compile against @authority/delegation as that piece publishes it until those extensions are made, and naming them is the point of showing the code. The clinical permission grammar and the person-rooted constructor; the root-issuance path bound to the order signature; the boundary check a FHIR-facing service performs before the operation at hand; and the projection from an authority chain to a disclosure-accounting entry. Cryptography, identifier generation and clock are injected, because a hospital's key management is not a library's business.

Note what is absent: there is no widen(), no merge(), and no way to build a Grant with a parent except through attenuate(). Note also that issueRootGrant accepts a ClinicianPrincipal and nothing else — a service account is not assignable to it, so the most likely way this design gets defeated in practice becomes a compile error rather than a code review comment.

src/clinical-grant.ts
import { attenuate, type AttenuationContext, type Grant } from "@authority/delegation";

/** A person, established by the identity provider and named in the clinical record.
 *  The nominal tag is what stops a service identity being assigned to a root grant. */
export interface ClinicianPrincipal {
  readonly kind: "clinician";
  /** Stable directory identifier. Distinct from any workstation or service identity. */
  readonly id: string;
  /** The identifier the medical record uses when it authenticates an entry. */
  readonly practitionerReference: string;
}

export interface AgentPrincipal {
  readonly kind: "agent";
  readonly id: string;
}

export type ClinicalPrincipal = ClinicianPrincipal | AgentPrincipal;

/** FHIR-shaped, because that is the namespace the resource servers speak. */
export type FhirResourceType =
  | "Encounter" | "DocumentReference" | "Observation"
  | "MedicationRequest" | "ServiceRequest" | "DiagnosticReport";

export type ClinicalVerb = "read" | "search" | "write";

/** The subject scope is what carries the minimum-necessary category. A permission that
 *  cannot say "this patient only" cannot express the duty at 45 CFR 164.514(d)(2)(i). */
export type SubjectScope =
  | { readonly kind: "patient"; readonly patientId: string }
  | { readonly kind: "encounter"; readonly encounterId: string };

export interface ClinicalPermission {
  readonly verb: ClinicalVerb;
  readonly resourceType: FhirResourceType;
  readonly scope: SubjectScope;
}

/** Purpose of use is a caveat, not an annotation, because 45 CFR 164.528(b)(2) requires the
 *  accounting entry to carry it and a purpose reconstructed afterwards is not evidence. */
export type PurposeOfUse = "treatment" | "payment" | "operations" | "research" | "patient-request";

export type ClinicalCaveat =
  | { readonly kind: "expires-at"; readonly epochMillis: number }
  | { readonly kind: "purpose-of-use"; readonly purpose: PurposeOfUse }
  | { readonly kind: "audience"; readonly resourceServer: string }
  | { readonly kind: "patient"; readonly patientId: string }
  /** Part 2 material must arrive with its notice. Carried so the boundary can refuse to
   *  release it to a hop that has not acknowledged the redisclosure restriction. */
  | { readonly kind: "part-2-restricted"; readonly consentReference: string }
  /** Counted state the verifier does not hold. Enforced by whoever holds the counter. */
  | { readonly kind: "max-invocations"; readonly count: number };

export type ClinicalGrant = Grant<ClinicalPermission, ClinicalCaveat>;

/** Permission containment. A parent covers a child when the verb and resource type match and
 *  the child's scope is not broader. Deliberately dull: containment is a security decision,
 *  and a clever implementation of it is a liability. */
export const covers = (parent: ClinicalPermission, child: ClinicalPermission): boolean => {
  if (parent.verb !== child.verb) return false;
  if (parent.resourceType !== child.resourceType) return false;
  if (parent.scope.kind === "patient" && child.scope.kind === "patient") {
    return parent.scope.patientId === child.scope.patientId;
  }
  if (parent.scope.kind === "encounter" && child.scope.kind === "encounter") {
    return parent.scope.encounterId === child.scope.encounterId;
  }
  return false;
};

export interface RootGrantRequest {
  readonly clinician: ClinicianPrincipal;
  readonly permissions: readonly ClinicalPermission[];
  readonly caveats: readonly ClinicalCaveat[];
  /** The order or record entry whose authentication this grant descends from. */
  readonly authenticatedEntry: { readonly reference: string; readonly signedAtMillis: number };
}

/** The only way to make a root. Its subject is a ClinicianPrincipal by type, so a batch job
 *  running under a service identity cannot become the head of an authority chain. */
export const issueRootGrant = (
  request: RootGrantRequest,
  ctx: AttenuationContext<ClinicalPermission, ClinicalCaveat>,
): ClinicalGrant => ctx.mintRoot({
  subject: request.clinician.id,
  subjectDisplay: request.clinician.practitionerReference,
  authority: { permissions: request.permissions, caveats: request.caveats },
  origin: request.authenticatedEntry.reference,
});

export interface HandoffRequest {
  readonly to: AgentPrincipal;
  /** Which of the parent's permissions to carry forward. Nothing else can appear. */
  readonly keep: readonly ClinicalPermission[];
  readonly addCaveats?: readonly ClinicalCaveat[];
}

/** Every handoff goes through here. attenuate computes the intersection of keep with the
 *  parent's authority; anything the parent did not hold is absent from the result. */
export const handOff = (
  parent: ClinicalGrant,
  request: HandoffRequest,
  ctx: AttenuationContext<ClinicalPermission, ClinicalCaveat>,
): ClinicalGrant => attenuate(parent, {
  subject: request.to.id,
  keep: request.keep,
  addCaveats: request.addCaveats ?? [],
}, ctx);

The reference implementation is @authority/delegation; what is shown here is the clinical instantiation of a parameterised form of it, not a second copy of the primitive — the general piece publishes Grant, AttenuationContext and verifyChain in their unparameterised shape, and the clinical types above assume the permission and caveat types have been lifted into type arguments. Cryptography, identifier generation, the clock and the workforce-class catalogue are all injected — a library has no business choosing a hospital's key management, and the catalogue belongs to the privacy office rather than to the codebase.

The control path, step by step

Eight steps, from a clinician's signature to a row in an accounting of disclosures. Nothing in this path calls a policy service or depends on telemetry being sampled in.

  1. The clinician authenticates an order. The moment 482.24(c)(2) already recognises, and where the root grant is minted: subject the practitioner, authority compiled from their workforce class, bounded to the patient in front of them, with a purpose caveat and a short expiry.
  2. The orchestrator receives the root grant, not a session token. The substitution that makes the rest work, and the one that touches the most existing code.
  3. The triage agent is handed a child. The orchestrator names which of the root's permissions to keep; the constructor computes the intersection; the child names its parent and is sealed by it.
  4. The triage agent delegates to retrieval. The same operation one level deeper — keeping only what retrieval needs, adding a five-minute expiry and an audience caveat naming the one server the grant may be presented to.
  5. The retrieval agent presents the chain at the boundary. Root first, leaf last, in a header, at a surface you own — your FHIR facade, your tool dispatcher, your agent gateway — because those are the only places you can put a check.
  6. The boundary verifies and decides. It recomputes the running intersection, checks the operation against it, evaluates the caveats it can and refuses the ones it cannot, and returns a decision naming the root subject and the purpose. One MAC per link, no I/O.
  7. The authority record is appended. Append-only, tamper-evident, cross-referenced by identifier to the AuditEvent the certified system writes for the same call. Two records, one join key — you are not modifying the certified log, and could not if you wanted to.
  8. The accounting entry is projected on demand. Date, recipient, description, purpose — and the human who authorised it. That last field is not required by 164.528(b)(2); it is the field that lets you defend the row.
Six layers, and the two most likely to be skipped The reference architecture, with the published rule each layer answers. Nothing here is new law; only layer three is new code. LAYER WHAT IT DOES ANCHORED IN 1 · Clinician authentication and order signature most-skipped The root grant is minted here — at the act of authenticating an order, not at session login. Its subject is a person. Without this layer the chain terminates in a service account and proves nothing about anyone. 42 CFR 482.24(c)(2) 21 CFR 11.300(a) 45 CFR 164.312(a)(2)(i) 2 · Minimum-necessary catalogue Maps each workforce class to the categories of information it needs, and compiles those categories into permission sets a machine can intersect. Your privacy office already wrote this artefact, in prose. 45 CFR 164.514(d)(2)(i) 45 CFR 164.514(d)(2)(ii) 45 CFR 164.502(b) 3 · Attenuation runtime the only new code Every handoff mints a strictly weaker credential naming its parent. The constructor computes an intersection, so a child cannot hold more than its parent — the runtime shape of the subcontractor-assurance rule. 45 CFR 164.502(e)(1)(i) 45 CFR 164.502(e)(1)(ii) — an analogy, not a mandate 4 · Boundary verification at surfaces you own Recomputes the chain from the root and decides before the call runs. No network call, no policy round trip. It never trusts the authority the leaf credential claims for itself; it derives it from the chain above. 21 CFR 11.10(g) — the check sits at the operation at hand, not at session entry 5 · Authority record beside the audit log Append-only, tamper-evident, cross-referenced to the audit entries the certified system already writes. It does not replace them — you cannot add a field to a certified system's log, and you do not need to. 45 CFR 170.315(d)(2) 45 CFR 170.315(d)(10) 21 CFR 11.10(e) 6 · Evidence and retention plane most-skipped Archived verification keys, a frozen serialisation format with a version number, and a written procedure a qualified person can certify. Built for a reader who has the record, no running system, and no memory of you. 45 CFR 164.316(b)(2)(i) 42 CFR 482.24(b)(1) 21 CFR 312.62(c) · FRE 902(13) Layers one and six are the ones I expect to get cut, because neither produces a demo. Cut layer one and the chain ends in a service account. Cut layer six and the chain cannot be checked by the only reader who matters — the one who was not there.

The architecture behind those eight steps has six layers, and it is worth naming which is actually new. Layer three is new code. Layer two is a compilation of a document your privacy office has already written. Layers four and five are integrations into surfaces you already operate. Layers one and six are the ones I would expect a team to cut, because neither produces anything demonstrable in a sprint review — and each is fatal in a different way. I have no survey behind that expectation; treat it as a prediction rather than a finding. Cut layer one and every chain terminates in a service account, which is the failure the design existed to prevent. Cut layer six and the chains verify beautifully today and are inert in year eight.

Where this design fails

Eight failures. The first three are the ones that would keep me up.

One: a narrower authority is not always a smaller harm. Authority is ordered by set inclusion. Clinical consequence is not. A grant restricted to read on a single patient's psychotherapy notes is strictly weaker than one permitting read across that patient's routine laboratory values, and far more damaging if it leaks. Attenuation gives a provable statement about the extent of a permission and none at all about its sensitivity. Anyone reading a monotone chain as a risk gradient has made an error this design does nothing to prevent, and the sensitivity ordering — psychotherapy notes and genetic results are not interchangeable with a haemoglobin value — has to come from elsewhere and be enforced separately.

Two: the root grant is minted at the busiest moment in a clinician's day. Everything below the root is bounded by the root, so a broad root is a perfectly monotone chain descending from a bad start — and the root is minted while a doctor is signing an order between patients. Nobody makes a considered scoping decision there, so the scoping has to come from the workforce-class catalogue, which means the quality of this entire control is the quality of a document the privacy office wrote for a paper-era workflow and has not revisited. The design converts a scattered problem into one visible, signed, reviewable artefact, which is better. It is not a solution: a team that deploys this without re-deriving its catalogue has automated a stale judgement at machine speed.

Three: attenuation bounds extent and does nothing about misuse inside it. The component doing the delegating is a model in a tool loop. A model that has been successfully injected can still delegate correctly — monotonically, producing a valid child grant naming an attacker-chosen sub-agent — as long as it stays inside the authority it holds. The chain will verify and the record will be complete, accurate, and an accurate record of an attack. What the design buys is a bound and a receipt: damage is capped at what the compromised node legitimately held, and afterwards you can state precisely what that was and when it started, which is a better answer to 164.402 factor (ii) than the alternative. It is not prevention and must never be sold as prevention.

Four: revocation and offline verification pull in opposite directions, and clinicians leave. A child names its parent, so revoking a parent ought to invalidate the line — but checking that requires the verifier to consult something, which is the one thing offline verification forbids on the hot path. The answer is short lifetimes plus a revocation epoch published with the root key, and it is blunt: it revokes everything under a root rather than one branch, at a latency equal to the cache interval. The clinical case that exposes it is mundane and constant: a physician's privileges are suspended, and the chains issued under their authority an hour earlier remain valid until they expire. Short root lifetimes are the whole mitigation, and they are exactly what a long-running care-coordination workflow will fight you on.

Five: chains grow, and re-rooting discards the ancestry the record exists to keep. Verification is linear in depth and the header grows with it, carrying caveats that never shrink. A referral triage is fine; a longitudinal care-management agent that delegates on every iteration for a week is not. The mitigation is re-rooting — a fresh root carrying the current effective authority — and re-rooting throws away exactly the ancestry a disclosure accounting needs. The resolution is to write the compaction point into the record as an explicit re-root edge, so the ancestry lives there rather than in the credential. That is a correct answer and an admission: past the compaction point the chain alone is no longer sufficient evidence.

Six: the caveats that matter most clinically are the ones that cannot be checked offline. You can see it in the boundary code: a max-invocations caveat returns a refusal, because counting is state the verifier does not hold. Break-glass counters, per-encounter access budgets and bulk-export rate limits are all in the same position — expressible, carried, recorded, and enforceable only by whoever holds the counter. So a caveat can sit in a chain looking enforced and be silently unenforced. The code above refuses rather than ignores, which is the right default and the first thing somebody will turn off.

Seven: the authority record is itself protected health information, with the longest retention in your estate. This is the failure I would raise first in a design review and the one least often anticipated. A record naming patients, clinicians, resource types and purposes across every agent action, retained for six years or more under 164.316(b)(2)(i), is a high-value target that did not exist before you built it. It needs the same safeguards as the medical record, it enlarges every business-associate agreement that touches it, and under 164.528 it may itself become the subject of a patient's request. Building the evidence layer creates a new disclosure surface, and that cost belongs in the business case rather than the appendix.

Eight: you cannot make a certified system record any of this. If your FHIR server is a vendor product certified under 170.315(d)(2), its audit log is what the certification says it is, and you cannot add a field to it. The authority record therefore lives beside the certified log, joined by identifier, which means your evidence is now two artefacts that must be correlated rather than one that is complete. Every reconciliation problem that implies — clock skew, dropped writes, identifier drift — is now yours. The design is honest about this because the alternative is a fantasy in which vendors add a delegation element to a resource that does not define one.

There is also a whole category the design says nothing about, which should be stated rather than implied. These are bearer credentials unless you add a possession binding — possession of the terminal seal is authority, which is the price of offline verification — and the binding costs a signature per request. It does not authenticate the human at the root; that is the identity provider's job. It does not stop an agent reading data it was legitimately granted and putting it somewhere it should not go. It does not evaluate whether an order was clinically appropriate, and it makes no claim about a model's behaviour. It is a mechanism for carrying and constraining authority, which is a small and specific thing to be good at.

What it costs

Four kinds of cost: on the request, on the store, on the operators, and on the migration.

On the request. Verification is one MAC computation and one canonical serialisation per link — linear in chain depth with a small constant, pure computation, no I/O and no lock. That is a property of the algorithm and I am comfortable asserting it. What I will not assert is a microsecond figure, because I have not measured this implementation under a clinical workload. The measurement is planned rather than performed: p50 and p99 verification latency across chain depths one through sixteen, with and without a possession-proof signature, on a single core, against a synthetic workload shaped like referral triage. The prediction under test is that verification without possession proof stays below the cost of parsing the FHIR bundle it accompanies at every depth in that range; if it does not hold, that is worth publishing too. The wire cost is more predictable and less comfortable: each link carries two principal identifiers, a permission list and a seal, so a chain of eight is a header measured in kilobytes, and intermediate proxies with header limits will bite before latency does.

On the store. The cost specific to this sector, and the one most likely to be underestimated. An authority record retained for six years under 164.316(b)(2)(i), or longer where a Part 11 clock attaches, is not a log with a thirty-day rotation. It is an archive, it holds protected health information, and it must stay readable and verifiable after the storage format, the key material and the team have all turned over. Budget it as records management rather than observability — the failure mode of the latter is that somebody sets a retention policy in a dashboard and year one's evidence is gone in year two.

On the operators. A key with a rotation policy that must not break verification of chains issued under the previous key, which in practice means archiving public keys with the record rather than rotating them away. A revocation epoch somebody must be able to bump under pressure. A root-issuance path wired into a clinical application, with a human-facing surface, because a root grant is a decision rather than a config value. And a new class of incident — the chain does not verify — with its own runbook, its own false positives and its own fail-open-versus-fail-closed decision, which someone senior has to make deliberately in a setting where failing closed can delay care. None of it is exotic. All of it is headcount.

On the migration. A grant is worthless until something verifies it, and the enforcement points you own are your own surfaces — the tool dispatcher, the FHIR facade, the agent gateway — not the vendor EHR. The order that works is: emit and enforce nothing, so the record fills up and you can see the shape of the delegation your workflows actually produce; then verify at one boundary you own, logging failures and allowing the call; then flip that boundary to failing closed; then widen. Every step is reversible except the last, and the value arrives at step one. Budget the emit-only step in weeks and the first failing-closed boundary in months, and expect the argument about failing closed to take longer than the implementation.

One thing to watch while you plan: the Security Rule overhaul is still proposed, not law. The NPRM was published on 6 January 2025 with the comment period closing 7 March 2025, and as of August 2026 the effective text at 45 CFR 164.312 remains the January 2013 version — which is what everything above is derived from. Design against the current rule, and re-read it before publishing anything of your own in case the proposal has been finalised in the interim.

If you had a week

Build three things and deliberately skip the rest.

  1. The permission type, the constructor, and both property tests. The clinical permission grammar, the person-typed root, handOff, and the two properties from the fourth tab: that no child holds anything its parent did not, and that a chain of any depth still resolves to the person at the root. Two days, and the part that has to be right, because everything else is downstream of the invariant holding. Without the property tests you do not have a guarantee; you have an intention.
  2. Emission from one boundary, in shadow. Pick the place your system hands work from one component to another — for most estates the tool dispatcher — and emit an authority edge on every handoff, cross-referenced to the audit entry the certified system writes. Enforce nothing, verify nothing, just write the graph. Inside a week you will have what no dashboard in your estate shows: the real shape of delegation in a live workflow, including the hops nobody knew were there and the ones that resolve to a service account.
  3. One resource server that verifies, logs, and allows. The least frightening one. Verify the chain and log the result beside the existing authorization decision. You are hunting for the disagreements — calls your current authorization permits that the chain would not, and the reverse. They will tell you more about your minimum-necessary catalogue than any amount of whiteboarding.

What not to build in that week: revocation infrastructure, possession binding, a purpose ontology, a caveat language, a user interface, compaction, and anything at all touching 42 CFR part 2 — that last one needs a lawyer in the room before it needs an engineer, because the redisclosure restriction is a legal obligation on the recipient rather than a flag on a record. Every one of those is real work that a real deployment eventually needs, and every one of them is a way to spend a week without ever seeing the graph. See the graph first. It reliably changes what you think you are building.

The last thing I would say to a team weighing this up is that the case for it is not primarily a security case. Attenuation caps damage, which is worth something, and the failure analysis above should make clear how much and how little. The argument that carries in this sector is evidentiary. Every other question about a clinical system got an answer a decade ago — what happened, when, how long it took, whether it worked. On whose authority is the one question still answered by asking somebody who was there, at exactly the moment nobody who was there is still available, and in front of a reader whose job is to find that answer insufficient.