You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Design Discussion: FHIR Subscriptions (R4, R4B, R5, and R6)
Summary
This discussion outlines the proposed design for implementing topic-based FHIR Subscriptions in the Helios FHIR Server across all supported FHIR versions, based on the FHIR Subscriptions Framework. For R4 and R4B, the implementation follows the Subscriptions R5 Backport Implementation Guide (v1.2.0-ballot). For R5 and R6, the implementation uses the native Subscriptions Framework defined in the core specification.
We are sharing this design publicly to solicit feedback before implementation begins. If you have experience implementing FHIR Subscriptions - as a server, client, or intermediary - we would especially welcome your input on the trait abstractions and channel model described below.
Background
Why Topic-Based Subscriptions?
The legacy Subscription model (DSTU2 through R4) uses a query-defined approach: clients post a Subscription resource containing a criteria string, and the server must continuously re-evaluate that query against its dataset to determine when to fire notifications. This approach has well-documented shortcomings:
Scalability: Re-running arbitrary queries on every data change does not scale with large datasets or many concurrent subscribers.
Opaque discovery: Servers that had restrictions on supported queries had no standardized way to advertise those restrictions.
Coarse event granularity: Clients could not distinguish why a result set changed (e.g., create vs. update vs. delete).
No batching: Each discrete event required a separate notification.
Mandatory re-query: Notifications carried no payload, forcing clients to query back for the changed data.
The R5 redesign addresses all of these issues by introducing a topic-based model with three key changes:
SubscriptionTopic - a server-defined resource that declares what events are available for subscription, including resource triggers, filters, and notification shapes.
Revised Subscription - clients subscribe to a specific topic (by canonical URL) rather than supplying an arbitrary query.
Structured notification bundles - a SubscriptionStatus resource accompanies every notification, providing metadata about the event, and bundles can carry the triggering resources directly.
Because R5 adoption is still maturing across the ecosystem, the Subscriptions R5 Backport IG defines a standardized mechanism to bring this functionality to R4 and R4B servers. This is the specification we intend to implement.
Scope of This Implementation
We plan to support topic-based subscriptions across all four FHIR versions supported by HFS:
FHIR R4 - using extensions and profiles defined by the Backport IG (e.g., backport-filter-criteria, backport-payload-content, backport-channel-type). In R4, SubscriptionStatus is represented as a Parameters resource conforming to the Backport SubscriptionStatus profile, and SubscriptionTopic is represented as a Basic resource with cross-version extensions.
FHIR R4B - using the native SubscriptionTopic and SubscriptionStatus resources introduced in R4B, supplemented by the Backport IG where needed.
FHIR R5 - using the native SubscriptionTopic, Subscription, and SubscriptionStatus resources as defined in the R5 specification. R5 is the origin of the topic-based design; no backport extensions are needed. R5 also introduces the subscription-notification bundle type, replacing the history bundle workaround used in earlier versions.
FHIR R6 - using the R6 subscription resources, which refine and stabilize the R5 model. As R6 matures, we will track any changes to SubscriptionTopic, notification bundle structure, and related operations. The version-agnostic engine design ensures R6 support can be added with minimal friction.
All four versions will share the same core engine, with version-specific adapters handling the differences in resource representation. R4 and R4B use the Backport IG artifacts; R5 and R6 use the native specification directly.
Architecture Overview
The design follows the same modular philosophy used throughout HFS: define abstract trait boundaries for each major concern, then provide concrete implementations that can be composed at build time via feature flags (R4, R4B, R5, R6).
The subscription system decomposes into five primary concerns:
Concern
Responsibility
Topic Registry
Stores and advertises SubscriptionTopic definitions; evaluates whether a resource change matches a topic's triggers.
Subscription Manager
CRUD lifecycle for Subscription resources; status tracking (requested → active → error → off); filter validation.
Event Evaluator
On each resource write (create/update/delete), determines which active subscriptions match and produces notification events.
Notification Builder
Constructs notification Bundle resources (type history) with the appropriate SubscriptionStatus entry and payload entries, respecting the empty / id-only / full-resource content level.
Channel Dispatcher
Delivers notification bundles to subscriber endpoints via the configured channel (REST-hook, WebSocket, Email, FHIR Messaging, or custom).
Data Flow
Resource Write (create/update/delete)
│
▼
┌─────────────────┐
│ Event Evaluator │ ── For each active Subscription, test the
│ │ topic triggers + filter criteria
└────────┬────────┘
│ matched subscriptions + event metadata
▼
┌─────────────────────┐
│ Notification Builder│ ── Build Bundle(type=history) with
│ │ SubscriptionStatus + payload entries
└────────┬────────────┘
│ notification Bundle
▼
┌─────────────────────┐
│ Channel Dispatcher │ ── Deliver via rest-hook, websocket,
│ │ email, messaging, or custom channel
└─────────────────────┘
Proposed Rust Traits
Below are the core trait definitions we are proposing. These are designed to be version-agnostic at the engine level, with version-specific types provided by the helios-fhir crate's r4 and r4b modules.
SubscriptionTopicRegistry
The topic registry is responsible for storing topic definitions and evaluating whether a given resource change matches a topic's triggers.
use async_trait::async_trait;/// A version-agnostic description of a resource event.#[derive(Debug,Clone)]pubstructResourceEvent{/// The resource type (e.g., "Encounter", "Patient").pubresource_type:String,/// The resource ID.pubresource_id:String,/// The type of interaction that produced this event.pubinteraction:InteractionType,/// The resource content after the interaction (None for delete).pubresource:Option<serde_json::Value>,/// The resource content before the interaction (None for create).pubprevious_resource:Option<serde_json::Value>,}#[derive(Debug,Clone,Copy,PartialEq,Eq)]pubenumInteractionType{Create,Update,Delete,}/// Describes a topic that matched a resource event, along with/// the specific trigger that fired.#[derive(Debug,Clone)]pubstructTopicMatch{/// Canonical URL of the matching SubscriptionTopic.pubtopic_url:String,/// The focus resource type for this trigger.pubfocus_resource_type:String,/// Optional additional context resource types defined by the topic's/// notificationShape.include.pubinclude_resource_types:Vec<String>,}#[async_trait]pubtraitSubscriptionTopicRegistry:Send + Sync{/// Return all topic canonical URLs supported by this server.asyncfnlist_topics(&self) -> Result<Vec<String>,SubscriptionError>;/// Evaluate a resource event against all registered topics./// Returns the set of topics whose triggers match.asyncfnevaluate(&self,event:&ResourceEvent,) -> Result<Vec<TopicMatch>,SubscriptionError>;/// Retrieve the topic definition for inclusion in CapabilityStatement/// or direct read. The returned value is version-specific:/// - R4: a `Basic` resource with cross-version extensions/// - R4B/R5/R6: a `SubscriptionTopic` resourceasyncfnget_topic(&self,canonical_url:&str,) -> Result<Option<serde_json::Value>,SubscriptionError>;}
SubscriptionManager
Manages the lifecycle of Subscription resources and tracks their runtime state.
/// The possible states of a subscription.#[derive(Debug,Clone,Copy,PartialEq,Eq)]pubenumSubscriptionStatus{Requested,Active,Error,Off,}/// Describes the payload content level requested by the subscriber.#[derive(Debug,Clone,Copy,PartialEq,Eq)]pubenumPayloadContent{Empty,IdOnly,FullResource,}/// Channel configuration for a subscription.#[derive(Debug,Clone)]pubstructChannelConfig{pubchannel_type:ChannelType,pubendpoint:Option<String>,pubpayload_mime_type:Option<String>,pubpayload_content:PayloadContent,pubheaders:Vec<String>,pubheartbeat_period:Option<u32>,pubtimeout:Option<u32>,pubmax_count:Option<u32>,}#[derive(Debug,Clone,PartialEq,Eq)]pubenumChannelType{RestHook,Websocket,Email,Message,Custom(String),}/// A version-agnostic view of a subscription's essential fields.#[derive(Debug,Clone)]pubstructSubscriptionRecord{pubid:String,pubstatus:SubscriptionStatus,/// Canonical URL of the SubscriptionTopic.pubtopic_url:String,/// Filter criteria expressions (backport-filter-criteria extension values).pubfilters:Vec<String>,pubchannel:ChannelConfig,/// Monotonically increasing event counter for this subscription.pubevents_since_start:u64,}#[async_trait]pubtraitSubscriptionManager:Send + Sync{/// Create a new subscription. The manager validates the topic URL,/// filter criteria, and channel configuration. Returns the persisted/// record (which may differ from the request if the server adjusted/// parameters such as heartbeat period).asyncfncreate(&self,subscription:&serde_json::Value,) -> Result<SubscriptionRecord,SubscriptionError>;/// Retrieve a subscription by ID.asyncfnget(&self,id:&str) -> Result<Option<SubscriptionRecord>,SubscriptionError>;/// Update the status of a subscription (e.g., after a successful/// handshake, or when an error is detected).asyncfnset_status(&self,id:&str,status:SubscriptionStatus,error:Option<String>,) -> Result<(),SubscriptionError>;/// Return all active subscriptions for a given topic URL.asyncfnactive_subscriptions_for_topic(&self,topic_url:&str,) -> Result<Vec<SubscriptionRecord>,SubscriptionError>;/// Increment the event counter and return the new value.asyncfnincrement_event_count(&self,id:&str,) -> Result<u64,SubscriptionError>;/// Execute the $status operation for a subscription.asyncfnstatus_operation(&self,id:&str,) -> Result<serde_json::Value,SubscriptionError>;/// Delete a subscription.asyncfndelete(&self,id:&str) -> Result<(),SubscriptionError>;}
NotificationBuilder
Constructs notification bundles according to the Backport IG profiles. This is the component where R4 vs R4B differences are most visible.
/// Metadata about a single notification event.#[derive(Debug,Clone)]pubstructNotificationEvent{pubevent_number:u64,pubtimestamp:Option<String>,pubfocus_resource:Option<String>,// Reference URLpubadditional_context:Vec<String>,// Reference URLs}#[derive(Debug,Clone,Copy,PartialEq,Eq)]pubenumNotificationType{Handshake,Heartbeat,EventNotification,QueryStatus,QueryEvent,}#[async_trait]pubtraitNotificationBuilder:Send + Sync{/// Build a notification Bundle for the given subscription and events.////// The returned Bundle:/// - R4/R4B: `type` = `history`/// - R5/R6: `type` = `subscription-notification`/// - entry[0] is the SubscriptionStatus (Parameters in R4,/// SubscriptionStatus resource in R4B/R5/R6)/// - subsequent entries depend on payload content level:/// - `empty`: no additional entries/// - `id-only`: entries with fullUrl and request, no resource/// - `full-resource`: entries with the full resource in entry.resourceasyncfnbuild_notification(&self,subscription:&SubscriptionRecord,notification_type:NotificationType,events:&[NotificationEvent],resources:&[serde_json::Value],) -> Result<serde_json::Value,SubscriptionError>;}
ChannelDispatcher
Delivers constructed notification bundles to subscriber endpoints.
/// The result of attempting to deliver a notification.#[derive(Debug)]pubenumDeliveryResult{/// The endpoint accepted the notification.Success,/// The endpoint returned an error. The server should update the/// subscription status accordingly.Failed{status_code:Option<u16>,message:String},/// The channel does not support acknowledgement (e.g., email).BestEffort,}#[async_trait]pubtraitChannelDispatcher:Send + Sync{/// Return the channel types supported by this dispatcher.fnsupported_channel_types(&self) -> Vec<ChannelType>;/// Deliver a notification bundle to the subscriber's endpoint.asyncfndispatch(&self,subscription:&SubscriptionRecord,notification_bundle:&serde_json::Value,) -> Result<DeliveryResult,SubscriptionError>;/// Perform the handshake sequence for a newly created subscription./// For REST-hook, this sends a handshake notification to the endpoint./// For WebSocket, this is a no-op (handshake occurs on bind).asyncfnhandshake(&self,subscription:&SubscriptionRecord,handshake_bundle:&serde_json::Value,) -> Result<DeliveryResult,SubscriptionError>;}
SubscriptionEngine
The orchestrator that ties everything together. This is invoked by the server's resource write path.
#[async_trait]pubtraitSubscriptionEngine:Send + Sync{/// Called by the server after a resource write has been committed./// This method:/// 1. Evaluates the event against all registered topics./// 2. For each matching topic, finds all active subscriptions./// 3. For each matching subscription, applies filter criteria./// 4. Builds and dispatches notifications for all matched subscriptions.asyncfnon_resource_event(&self,event:ResourceEvent,) -> Result<(),SubscriptionError>;/// Send heartbeat notifications for all subscriptions that are due.asyncfnsend_heartbeats(&self) -> Result<(),SubscriptionError>;}
The core traits operate on version-agnostic types (SubscriptionRecord, ResourceEvent, etc.) and use serde_json::Value at the FHIR resource boundary. The NotificationBuilder is the primary point of version-specific divergence:
R4: Notification bundles use a Parameters resource (conforming to the backport-subscription-status-r4 profile) as entry[0]. The Subscription.criteria element carries the topic canonical URL. Extensions like backport-filter-criteria, backport-payload-content, backport-channel-type, backport-heartbeat-period, backport-timeout, and backport-max-count are used on the Subscription resource. Bundle type is history.
R4B: Notification bundles use a native SubscriptionStatus resource as entry[0]. The SubscriptionTopic resource exists natively, though this guide supplements it with additional extensions where needed. Bundle type is history.
R5: Notification bundles use the native SubscriptionStatus resource and the new subscription-notification bundle type. SubscriptionTopic and Subscription are used directly without backport extensions. The Subscription resource in R5 has dedicated elements for filter criteria, payload content, channel type, heartbeat period, timeout, and max count - no extensions needed.
R6: Expected to follow the R5 model with incremental refinements. The adapter will track any structural changes to SubscriptionTopic or notification bundle requirements as R6 stabilizes.
All adapters are compiled behind #[cfg(feature = "R4")], #[cfg(feature = "R4B")], #[cfg(feature = "R5")], and #[cfg(feature = "R6")] feature gates, consistent with the existing HFS multi-version model.
2. In-Band and Out-of-Band Subscription Management
The Subscriptions Framework defines two management styles:
In-band: Clients create and manage Subscription resources via the FHIR REST API. The server exposes SubscriptionTopic for discovery and supports CRUD on Subscription.
Out-of-band: Subscriptions are managed externally (e.g., through an admin UI or a management service). The server may or may not expose Subscription resources via the FHIR API.
Our initial implementation will focus on in-band management, as it is the most common use case and has the strictest conformance requirements. Out-of-band support can be layered on later without changing the core engine.
3. Channel Support Roadmap
We plan to implement channels in the following order:
Phase
Channel
Rationale
Phase 1
rest-hook
The de facto default. Most widely implemented. Exercises the full notification pipeline including handshake.
Phase 2
websocket
Important for clients that cannot expose an HTTP endpoint (mobile, browser). Requires the $get-ws-binding-token operation and token-based authentication.
Phase 3
email
Useful for public health and non-application use cases. Lower priority for initial release.
Phase 4
message (FHIR Messaging)
For non-RESTful server-to-server communication. Considered draft in the Backport IG.
Custom channels will be supported via the ChannelDispatcher trait - third parties can implement the trait for any delivery mechanism (e.g., Kafka, AMQP, cloud-native pub/sub).
4. Payload Content Strategy
The three payload content levels (empty, id-only, full-resource) represent a security-vs-convenience tradeoff:
empty: No PHI in the notification. Client must query back. Lowest risk, highest latency.
id-only: Resource references included but not resource bodies. Low PHI risk. Good balance for most scenarios.
full-resource: Complete resources in the notification bundle. Highest convenience, but only appropriate over secure channels.
Our server will support all three levels. The server will reject subscriptions requesting full-resource over non-TLS channels (plain HTTP), consistent with the security guidance in the IG. The default, if not specified by the client, will be id-only.
5. Error Handling and Delivery Semantics
The Backport IG defines best-effort (unreliable) delivery as the baseline for all standard channels. Our implementation will:
Track delivery failures per-subscription and surface them via SubscriptionStatus.error.
Retry failed REST-hook notifications with exponential backoff (configurable max retries).
Transition subscriptions to error status after repeated failures, and to off after a configurable threshold.
Expose errors to clients via the $status operation.
6. CapabilityStatement Integration
Conformant servers must advertise subscription support in their CapabilityStatement. The details vary by version:
R4: Declare Subscription resource support, the $status operation, the CapabilityStatement SubscriptionTopic Canonical extension for topic discovery, and reference the Backport Subscription profile in rest.resource.supportedProfile.
R4B: Declare Subscription and SubscriptionTopic resource support, plus the $status operation. Reference Backport IG profiles where applicable.
R5/R6: Declare Subscription, SubscriptionTopic, and SubscriptionStatus resource support, plus the $status and $events operations. Use the native CapabilityStatement subscription elements.
This will be integrated into the existing HFS CapabilityStatement generation pipeline, with version-specific builders.
Conformance Targets
FHIR R4
Per the Backport IG, a conformant R4 server:
SHALL support the Subscription resource (read/write).
SHALL support the $status operation on Subscription.
SHOULD support topic discovery via the CapabilityStatement SubscriptionTopic Canonical extension.
SHALL support at least one channel type and SHOULD include one from the IG.
SHALL support at least one payload type.
FHIR R4B
Per the Backport IG, a conformant R4B server:
SHALL support the Subscription resource (read/write).
SHALL support the $status operation on Subscription.
SHALL support the SubscriptionTopic resource (read/search).
SHALL support at least one channel type and SHOULD include one from the IG.
SHALL support at least one payload type.
FHIR R5
Per the R5 Subscriptions Framework, a conformant server supporting in-band management:
SHALL support the SubscriptionTopic resource (read/search).
SHALL support the Subscription resource (read/create/update/delete; SHOULD support search).
SHALL support the $status operation on Subscription.
SHALL support at least one channel type and SHOULD include one from the core-defined types.
SHALL support at least one payload type.
FHIR R6
R6 conformance requirements are expected to follow the R5 model. We will track the R6 specification as it matures and update conformance targets accordingly.
Open Questions
We'd like community input on the following:
Filter criteria evaluation - The Backport IG allows topics to define filters using FHIR search parameters. Should we implement filter evaluation by reusing HFS's existing search parameter indexing, or build a dedicated lightweight evaluator optimized for subscription matching?
WebSocket connection multiplexing - The IG recommends that a single WebSocket connection be able to authenticate to multiple Subscription resources (given browser limits of ~6 concurrent connections). What are implementers' preferred patterns for managing multi-subscription WebSocket sessions?
Event batching - The framework supports batching multiple events into a single notification bundle. What heuristics should we use to decide when to batch vs. send immediately? Time-window based? Count-based? Configurable per-subscription?
Custom channel extensibility - We plan to expose the ChannelDispatcher trait publicly so that downstream consumers of the helios-fhir-subscriptions crate can plug in custom channels. Is there interest in first-party support for specific messaging systems (Kafka, NATS, etc.)?
Subscription persistence - Should subscription state be stored in the same backend as FHIR resources (following the existing HFS storage abstraction), or in a dedicated lightweight store optimized for the subscription engine's access patterns?
We welcome all feedback - whether on the high-level architecture, the trait boundaries, specific design decisions, or the open questions above. Please comment below or reach out directly.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
-
Design Discussion: FHIR Subscriptions (R4, R4B, R5, and R6)
Summary
This discussion outlines the proposed design for implementing topic-based FHIR Subscriptions in the Helios FHIR Server across all supported FHIR versions, based on the FHIR Subscriptions Framework. For R4 and R4B, the implementation follows the Subscriptions R5 Backport Implementation Guide (v1.2.0-ballot). For R5 and R6, the implementation uses the native Subscriptions Framework defined in the core specification.
We are sharing this design publicly to solicit feedback before implementation begins. If you have experience implementing FHIR Subscriptions - as a server, client, or intermediary - we would especially welcome your input on the trait abstractions and channel model described below.
Background
Why Topic-Based Subscriptions?
The legacy Subscription model (DSTU2 through R4) uses a query-defined approach: clients post a
Subscriptionresource containing a criteria string, and the server must continuously re-evaluate that query against its dataset to determine when to fire notifications. This approach has well-documented shortcomings:The R5 redesign addresses all of these issues by introducing a topic-based model with three key changes:
SubscriptionTopic- a server-defined resource that declares what events are available for subscription, including resource triggers, filters, and notification shapes.Subscription- clients subscribe to a specific topic (by canonical URL) rather than supplying an arbitrary query.SubscriptionStatusresource accompanies every notification, providing metadata about the event, and bundles can carry the triggering resources directly.Because R5 adoption is still maturing across the ecosystem, the Subscriptions R5 Backport IG defines a standardized mechanism to bring this functionality to R4 and R4B servers. This is the specification we intend to implement.
Scope of This Implementation
We plan to support topic-based subscriptions across all four FHIR versions supported by HFS:
backport-filter-criteria,backport-payload-content,backport-channel-type). In R4,SubscriptionStatusis represented as aParametersresource conforming to the Backport SubscriptionStatus profile, andSubscriptionTopicis represented as aBasicresource with cross-version extensions.SubscriptionTopicandSubscriptionStatusresources introduced in R4B, supplemented by the Backport IG where needed.SubscriptionTopic,Subscription, andSubscriptionStatusresources as defined in the R5 specification. R5 is the origin of the topic-based design; no backport extensions are needed. R5 also introduces thesubscription-notificationbundle type, replacing thehistorybundle workaround used in earlier versions.SubscriptionTopic, notification bundle structure, and related operations. The version-agnostic engine design ensures R6 support can be added with minimal friction.All four versions will share the same core engine, with version-specific adapters handling the differences in resource representation. R4 and R4B use the Backport IG artifacts; R5 and R6 use the native specification directly.
Architecture Overview
The design follows the same modular philosophy used throughout HFS: define abstract trait boundaries for each major concern, then provide concrete implementations that can be composed at build time via feature flags (
R4,R4B,R5,R6).The subscription system decomposes into five primary concerns:
SubscriptionTopicdefinitions; evaluates whether a resource change matches a topic's triggers.Subscriptionresources; status tracking (requested→active→error→off); filter validation.Bundleresources (typehistory) with the appropriateSubscriptionStatusentry and payload entries, respecting theempty/id-only/full-resourcecontent level.Data Flow
Proposed Rust Traits
Below are the core trait definitions we are proposing. These are designed to be version-agnostic at the engine level, with version-specific types provided by the
helios-fhircrate'sr4andr4bmodules.SubscriptionTopicRegistryThe topic registry is responsible for storing topic definitions and evaluating whether a given resource change matches a topic's triggers.
SubscriptionManagerManages the lifecycle of
Subscriptionresources and tracks their runtime state.NotificationBuilderConstructs notification bundles according to the Backport IG profiles. This is the component where R4 vs R4B differences are most visible.
ChannelDispatcherDelivers constructed notification bundles to subscriber endpoints.
SubscriptionEngineThe orchestrator that ties everything together. This is invoked by the server's resource write path.
Key Design Decisions
1. Version-Agnostic Engine, Version-Specific Adapters
The core traits operate on version-agnostic types (
SubscriptionRecord,ResourceEvent, etc.) and useserde_json::Valueat the FHIR resource boundary. TheNotificationBuilderis the primary point of version-specific divergence:Parametersresource (conforming to thebackport-subscription-status-r4profile) asentry[0]. TheSubscription.criteriaelement carries the topic canonical URL. Extensions likebackport-filter-criteria,backport-payload-content,backport-channel-type,backport-heartbeat-period,backport-timeout, andbackport-max-countare used on theSubscriptionresource. Bundle type ishistory.SubscriptionStatusresource asentry[0]. TheSubscriptionTopicresource exists natively, though this guide supplements it with additional extensions where needed. Bundle type ishistory.SubscriptionStatusresource and the newsubscription-notificationbundle type.SubscriptionTopicandSubscriptionare used directly without backport extensions. TheSubscriptionresource in R5 has dedicated elements for filter criteria, payload content, channel type, heartbeat period, timeout, and max count - no extensions needed.SubscriptionTopicor notification bundle requirements as R6 stabilizes.All adapters are compiled behind
#[cfg(feature = "R4")],#[cfg(feature = "R4B")],#[cfg(feature = "R5")], and#[cfg(feature = "R6")]feature gates, consistent with the existing HFS multi-version model.2. In-Band and Out-of-Band Subscription Management
The Subscriptions Framework defines two management styles:
Subscriptionresources via the FHIR REST API. The server exposesSubscriptionTopicfor discovery and supports CRUD onSubscription.Subscriptionresources via the FHIR API.Our initial implementation will focus on in-band management, as it is the most common use case and has the strictest conformance requirements. Out-of-band support can be layered on later without changing the core engine.
3. Channel Support Roadmap
We plan to implement channels in the following order:
rest-hookwebsocket$get-ws-binding-tokenoperation and token-based authentication.emailmessage(FHIR Messaging)Custom channels will be supported via the
ChannelDispatchertrait - third parties can implement the trait for any delivery mechanism (e.g., Kafka, AMQP, cloud-native pub/sub).4. Payload Content Strategy
The three payload content levels (
empty,id-only,full-resource) represent a security-vs-convenience tradeoff:empty: No PHI in the notification. Client must query back. Lowest risk, highest latency.id-only: Resource references included but not resource bodies. Low PHI risk. Good balance for most scenarios.full-resource: Complete resources in the notification bundle. Highest convenience, but only appropriate over secure channels.Our server will support all three levels. The server will reject subscriptions requesting
full-resourceover non-TLS channels (plain HTTP), consistent with the security guidance in the IG. The default, if not specified by the client, will beid-only.5. Error Handling and Delivery Semantics
The Backport IG defines best-effort (unreliable) delivery as the baseline for all standard channels. Our implementation will:
SubscriptionStatus.error.errorstatus after repeated failures, and tooffafter a configurable threshold.$statusoperation.6. CapabilityStatement Integration
Conformant servers must advertise subscription support in their
CapabilityStatement. The details vary by version:Subscriptionresource support, the$statusoperation, theCapabilityStatement SubscriptionTopic Canonicalextension for topic discovery, and reference the Backport Subscription profile inrest.resource.supportedProfile.SubscriptionandSubscriptionTopicresource support, plus the$statusoperation. Reference Backport IG profiles where applicable.Subscription,SubscriptionTopic, andSubscriptionStatusresource support, plus the$statusand$eventsoperations. Use the native CapabilityStatement subscription elements.This will be integrated into the existing HFS
CapabilityStatementgeneration pipeline, with version-specific builders.Conformance Targets
FHIR R4
Per the Backport IG, a conformant R4 server:
Subscriptionresource (read/write).$statusoperation onSubscription.CapabilityStatement SubscriptionTopic Canonicalextension.FHIR R4B
Per the Backport IG, a conformant R4B server:
Subscriptionresource (read/write).$statusoperation onSubscription.SubscriptionTopicresource (read/search).FHIR R5
Per the R5 Subscriptions Framework, a conformant server supporting in-band management:
SubscriptionTopicresource (read/search).Subscriptionresource (read/create/update/delete; SHOULD support search).$statusoperation onSubscription.FHIR R6
R6 conformance requirements are expected to follow the R5 model. We will track the R6 specification as it matures and update conformance targets accordingly.
Open Questions
We'd like community input on the following:
Filter criteria evaluation - The Backport IG allows topics to define filters using FHIR search parameters. Should we implement filter evaluation by reusing HFS's existing search parameter indexing, or build a dedicated lightweight evaluator optimized for subscription matching?
WebSocket connection multiplexing - The IG recommends that a single WebSocket connection be able to authenticate to multiple
Subscriptionresources (given browser limits of ~6 concurrent connections). What are implementers' preferred patterns for managing multi-subscription WebSocket sessions?Event batching - The framework supports batching multiple events into a single notification bundle. What heuristics should we use to decide when to batch vs. send immediately? Time-window based? Count-based? Configurable per-subscription?
Custom channel extensibility - We plan to expose the
ChannelDispatchertrait publicly so that downstream consumers of thehelios-fhir-subscriptionscrate can plug in custom channels. Is there interest in first-party support for specific messaging systems (Kafka, NATS, etc.)?Subscription persistence - Should subscription state be stored in the same backend as FHIR resources (following the existing HFS storage abstraction), or in a dedicated lightweight store optimized for the subscription engine's access patterns?
Related Resources
We welcome all feedback - whether on the high-level architecture, the trait boundaries, specific design decisions, or the open questions above. Please comment below or reach out directly.
Beta Was this translation helpful? Give feedback.
All reactions