Skip to content

Reference source · 0.1.0-dev.11

reference-host.md

docs/reference-host.md · Download original file

# Reference Host and queue integration

The reference implementation is intentionally small enough to inspect. It demonstrates the backend contract while keeping patient approval policy inside the Host. Its current data adapter is synchronous SQLite, rather than the PostgreSQL deployment originally proposed in the implementation plan. This reduces local setup and permits real restart/retry tests; neither database is required by OPRP.

## Components

| Source | Responsibility |
| --- | --- |
| [identity.ts](../src/identity.ts) | Separate native signing/encryption keys, development authority issuance, constrained trust and durable fresh signed status. |
| [discovery.ts](../src/discovery.ts) | Account grammar, signed delegation, domain-constrained discovery trust, HTTPS WebFinger retrieval. |
| [envelope.ts](../src/envelope.ts) | Experimental signed and encrypted messages with replay context and response binding. |
| [authorization.ts](../src/authorization.ts) | Decision-provider boundary, approval tasks, short JWTs, refresh, grants and scoped representation. |
| [host.ts](../src/host.ts) | Limited Patient/Observation operations, replay storage, transactional receipts and selected audits. |
| [sdk.ts](../src/sdk.ts) | Protected exchange, certificate-validating HTTPS transport, typed token requests and automatic refresh. |
| [queue.ts](../src/queue.ts) | Durable ordered write intents and recovery through Host receipts. |
| [change-feed.ts](../src/change-feed.ts) | Atomic accepted-change events, consistent snapshots, private cursors and visible-history retention. |
| [rotation.ts](../src/rotation.ts) | Old/new key ownership proof bound to requester, Host and rotation ID. |
| [archive.ts](../src/archive.ts) | Signed record archives, offline verification and independent authenticated encrypted backups. |
| [opaque.ts](../src/opaque.ts) | Host-internal authenticated encryption for cursors and refresh recovery receipts. |
| [migration-proof.ts](../src/migration-proof.ts) | Domain-separated signed plan/receipt chain, Host pins and historical verification evidence. |
| [migration.ts](../src/migration.ts) | Owner admission, inaccessible staging, source fences, irreversible release, atomic activation/cancellation and continuation. |
| [host-peer.ts](../src/host-peer.ts) | Protected Host-to-Host exchanges with caller/recipient/reply verification. |
| [directory.ts](../src/reference/directory.ts) | Independent address-provider binding store and verified monotonic cutover publication. |
| [fixtures.ts](../src/reference/fixtures.ts) | Entirely synthetic authorities, identities, representative and approval policies. |
| [https-server.ts](../src/reference/https-server.ts) | Loopback development HTTPS server and optional WebFinger route. |

No component enrolls real vendors, verifies legal documents, authenticates a patient, or manages production private-key custody. Those are Host and authority integration responsibilities, with interoperable security obligations still to be specified.

## Decision-provider contract

`DecisionProvider(request, {requester, actor}, now)` returns per-scope `allow`, `approval_required`, or `deny`, plus a positive maximum authorization duration. The Host validates requested scope/time, evaluates this result, applies representative limits, and grants at most the requested period. A denied scope denies the whole initial request. Any approval-required scope makes the whole request pending; only those scopes appear in `approval_required`.

This is an internal reference interface, not a mandatory policy language. A production Host might consult hosting agreements, standing patient choices, verified clinician relationships, jurisdiction rules, or another authorization service. It must never confuse an authenticated vendor with patient permission. An emergency role in a credential or requester statement does not intrinsically grant emergency access.

The demonstration policy permits a synthetic application's demographic reads for one day without patient interaction and requires approval for longer duration or additional scopes. It demonstrates the agreed conditional behavior; it does not establish a legal basis or a complete emergency-information profile.

## Conditional approval integration

The Host commits a pending request, lifecycle metadata and `approval_tasks` entry together. `approvalTasks(now)` exposes nonexpired queued tasks only to trusted local integration code. Each includes patient, application, optional actor, requested period/scopes, scopes requiring approval, and expiry. Repeating a logical request does not duplicate its task or consume another new-request quota.

The Host's worker delivers the task using its chosen application, authenticated website, or other channel. A completed authenticated decision calls `approve(applicationCredentialBody, requestId, authenticatedApprover, now)`. Approval rechecks the pending request, scope restrictions, current representative authority and maximum period. It marks the task completed and creates the grant atomically.

`deny` uses the same trusted approver check and closes the pending task. The requester can cancel its pending request through `cancel_authorization`; expiry and cancellation are terminal and never create a grant. Polling reevaluates standing policy and representative status. A newly sufficient policy can issue automatically without an approval event. Pending requests are bound to their original application signing key; explicit dual-key rotation rebinds them without changing scope or time. See [credential lifecycle](credential-lifecycle.md).

Reference `AuthorizationOptions` configure `pendingLifetime` (3,600 seconds), `requestWindow` (60 seconds), `maxRequestsPerWindow` (10 per application/patient), and `maxPendingPerPatient` (5). These are illustrative operator choices. New unique requests consume the rolling-window quota; identical retries do not. Rejected quotas create no notification task. Network-edge limits, issuer/application abuse handling, patient blocking and actual delivery workers remain necessary.

Do not expose trusted approval methods as unauthenticated routes or accept `authenticatedApprover` from an unverified HTTP field. The demo simulates that authenticated interaction. Real delivery, patient authentication, delivery acknowledgements/retries and comprehensive abuse defense still need implementation. No emails or other messages are sent by the prototype.

## Storage and recovery

Host state includes resources/versions, grants, authorization lifecycle, refresh hashes/encrypted recovery receipts, replay entries, operation receipts and exportable receipt context, requester key pins/rotation receipts, imported signed archive evidence, custody state/migration proofs/staging data, approval tasks, change events, cursor secrets/retention floors, and selected audit rows. Application identity and patient form part of each receipt's structured tuple identity. Mutations, receipts, accepted-change events and success audits commit together. Retrying an identical operation creates neither a second clinical entry nor a second event; changed content under the same ID fails.

For a persistent Host, pass a filesystem path instead of `:memory:` and supply the same verified Host identity and credentials after restart. The restart tests do this directly; there is no production key-loading/configuration service yet. SQLite WAL and synchronous operations provide a local transaction boundary, not a multi-node architecture.

Export creates a consistent signed narrow-profile archive under a separate export permission. Trusted local restore into an empty record preserves resource IDs/versions and retry receipts, starts destination feed state, and retains source history evidence without grants or secrets. Offline backup encryption has an independent owner recovery key. General/incremental archive support remains pending. Coordinated source cutover is implemented for the narrow profile with independently admitted destination state, transaction fences, signed release/activation and verified discovery publication. See [portability](portability.md) and [Host migration](host-migration.md).

The clinical database is plaintext. Backups and filesystem access must be protected before real records are used. No seed method belongs on an untrusted route. Receipts, events and audits contain clinical data and need retention/access policies. Refresh lookup stores hashes plus encrypted short-lived recovery receipts; cursor keys are service secrets persisted in the database. The SDK persistence hook exposes secret state to an application-owned protected store; the default example otherwise keeps client state in memory.

## Durable queue

Create `new DurableQueue(path, client)`, then call `createLab` or `updateDemographics`. The adapter commits intent and stable operation ID locally before transport. `flush()` attempts outstanding entries sequentially, refreshing through the SDK and creating a new envelope for each attempt. A lost response leaves the logical write pending; a later retry recovers the Host's receipt. After restart, entries left submitted become pending because their remote outcome is unknown.

States are `pending`, `submitted`, `committed`, `conflicted`, and `rejected`. A version conflict or permission failure blocks later entries; the adapter never silently overwrites remote data or sends later writes around an unresolved predecessor. Network failures remain pending. The application schedules retries/backoff, shows stale/uncommitted state, and supplies a reviewed conflict-resolution workflow. No automatic timer or background notification is created by this adapter.

The queue binds its database to an application subject, pins a Host route per patient and is designed for one worker in one process. `followMigration` changes only a verified selected patient route after SDK state saves; changing the shared client cannot silently redirect other queued patients. Fences, moved records, route mismatch and absent new authorization remain pending. Existing authority rejection or conflicts are not silently revived. See [Host migration](host-migration.md). It has no distributed lease or multi-process concurrency support. It preserves insertion order rather than implementing an arbitrary dependency graph, and supports only the two demonstrated mutations. Completed entries remain stored. Explicit cancellation/resolution and general atomic FHIR transaction support are future work.

Local queue resources and results are plaintext patient data under the requester's storage responsibility. The adapter is not encrypted caching or a mobile secure-storage solution. Refresh response-loss recovery is implemented separately through bounded rotation receipts and the SDK's awaited persistence hook; mutation receipts do not substitute for that mechanism. See [refresh recovery](refresh-recovery.md) and [synchronization](synchronization.md).

## Optional notification worker and receiver

The Host's `subscriptions` service registers grant-bound callback work; The change-feed append path applies grant visibility and enqueues coalesced hints in the same mutation transaction; policy transitions enqueue a minimal wake-up too. Run `host.subscriptions.deliver(clock)` explicitly from an operator-owned worker. Its default transport admits only conservatively checked public HTTPS destinations, performs a protected challenge, and rechecks authority before each delivery. Retry state and short shared-database leases survive normal restart.

Applications can use `NotificationReceiver` with a dedicated durable database, expected IDs/expiry and a pinned Host. Its handler commits delivery deduplication and inbox work before returning the protected reply. `pending()` exposes durable pull tasks; `complete(id, revision)` must follow atomic mirror/cursor persistence and cannot erase a later wake-up. This reference layer supplies no production scheduler, secure database adapter or automatic external patient messaging. See [subscriptions](subscriptions.md) for exact lifecycle/integration rules.

The SDKs now provide [durable mirrors](pagination-mirrors.md) separately from the callback receiver and outbound write queue. Complete a callback inbox revision only after mirror persistence succeeds.

## Visibility profile update

The Host advertises `visibility_profile: "oprp-visibility-1"`. [Visibility and cache invalidation](visibility.md) defines trusted grant policy, exact security labels, `SUBSETTED` field projections, write/export protection and required baseline/change `visibility_epoch`. Changed visibility invalidates prior cursors and clears SDK mirrors before a new baseline; disconnected clients cannot learn unknown permission changes. [Negative conformance](negative-conformance.md) records authenticated rejection tests. Host policy and approval delivery remain local integration choices.

## External clinical validation update

The [external clinical validation guide](external-clinical-validation.md) supplies six versioned R4 profiles and selected US Core 9.0.0 targets. The [durable validation guide](durable-clinical-validation.md) adds full offline dependency locking, finite local terminology and restartable intake/migration jobs. Setup requires Java and a verified dependency-cache copy. Structural-only mode advertises disabled terminology; local mode rejects unavailable systems and does not include broad licensed clinical vocabularies. Host authorization and conditional patient approval remain separate.