Fire Arrow Server 2.6.1
Fire Arrow Server 2.6.1 has been released.
- (security) A database error while reading the caller's identity returns
503instead of continuing as the Public role - (feature) The administration UI can set a product name, logo, and colors with
fire-arrow.ui.branding - (feature) The patient chart adds a Questionnaires tab of completed responses
- (bugfix) CarePlans that exhaust materialization retries are picked up by the reconcile sweeper
- (bugfix) A large materialization run no longer runs on the caller's request
- (bugfix) A timezone extension written as
valueStringis applied, so due Tasks can move toready - (bugfix) Comparing a CarePlan no longer logs a false warning about contained references
A Failed Identity Lookup Used the Public Role
After a caller authenticates, the server reads the identity resource that token maps to — a Patient, Practitioner, Device, or other record — and applies that caller's role. If the read cannot finish because the database is unavailable, the server should say so and let the client retry.
Until 2.6.1, a failed read was treated as "this identity does not exist." The request continued as the Public client role. Callers then usually received 403 Forbidden, because Public does not have their rules. A queue or subscription consumer that treats 403 as permanent stopped retrying and abandoned the message. An API token whose account-status read failed the same way was refused as if the account were disabled. A token used on behalf of another identity received 403 Forbidden when that identity's record could not be read. Where the Public role itself is allowed to read or write, the same failure authorized the request under Public rules for as long as the database error lasted.
| Situation | Until 2.6.1 | Starting with 2.6.1 |
|---|---|---|
| The identity resource cannot be read (for example the database is unavailable) | Public role, usually 403 Forbidden | 503, issue code transient. Retry the request. |
| An API token's account status cannot be read | 403 Forbidden, as if the account were disabled | 503, issue code transient. Retry the request. |
| An on-behalf-of identity cannot be read | 403 Forbidden | 500. The message asks the client to retry. |
| The lookup finishes and finds no identity | Public role | Public role |
| The account is disabled | 403 Forbidden | 403 Forbidden |
The account-status row applies when fire-arrow.authentication.disable-inactive-accounts is true (the default). Deployments that do not authenticate callers are unaffected.
A failed check of an identity the server has already cached keeps that cache entry. The next request reuses it, including when the server would otherwise create a new identity resource for the same caller.
Requests that still resolve as Public are counted on authorization.identity.public_fallback, with reason no_user_identity or unresolved_identity_resource. No configuration change is required.
Administration UI Branding
The administration UI can take its product name, logo, favicon, font, and colors from configuration. With fire-arrow.ui.branding omitted, the UI keeps the shipped Fire Arrow appearance.
When the block is present, product-name is required. Logo, favicon, font, and theme are optional. Any theme value you omit keeps its shipped value. Light and dark are set separately.
fire-arrow:
ui:
branding:
product-name: Clinic
logo:
url: https://cdn.example.com/logo.svg
theme:
light:
brand: "#0F6E56"
canvas: "#F7F5F2"
dark:
brand: "#3DDC97"
A remote asset must use https. Set either file or url on a logo, favicon, or font. The server refuses to start when the block is invalid: a missing product name, both file and url, a non-https URL, an unknown color, or a file over 2 MB. Logo files are SVG or PNG. Favicon files are PNG, ICO, or SVG. Fonts are WOFF2, and a font file also requires typography.font-family. File names in one block must be unique. The browser loads a local file from /admin/branding/<filename>.
The host of an https logo, favicon, or font is added to the administration UI content security policy (img-src for images, font-src for fonts).
Questionnaires on the Patient Chart
Opening a patient shows a Questionnaires tab after Clinical data and before any tabs from fire-arrow.ui.patient-chart. The tab lists that patient's QuestionnaireResponse resources with status=completed, newest authored first, twenty at a time. Further pages load as you scroll.
Each response is an accordion: the questionnaire title, when it was completed, and the answers. When the Questionnaire definition supplies scores for those answers, the row also shows the score out of its maximum and a severity label. Hidden, display, and calculated items are left out of that score. The definition is loaded by its canonical URL, or taken from contained when the response points at a contained questionnaire. Switching patients shows that patient's responses. The search runs as the signed-in user, so that user's authorization applies.
No configuration is required. A custom tab must not use the id questionnaires. A patient-chart document that does is rejected, and no custom tabs are shown.
Bug Fixes
CarePlans never received their Tasks
Materialization of a CarePlan's Tasks retries while another writer holds that plan. When those retries were used up — typical under a large wave of CarePlan writes — the server logged an error and stopped. Nothing recorded the plan for a later pass. The Tasks were never created unless someone edited the CarePlan or called $materialize or $renew-due-events.
Starting with 2.6.1, a run that uses up its retries records the CarePlan for the reconcile sweeper. The sweeper runs on fire-arrow.careplan-events.cleanup.reconcile-sweep-interval (default 5 minutes) and tries materialization again. A CarePlan that has been deleted is skipped. If recording the plan fails, the error log names $materialize and $renew-due-events as the manual recovery.
After upgrading, call $materialize or $renew-due-events for any CarePlan that never received its Tasks under an earlier version. Those plans were not recorded for the sweeper. Plans that exhaust their retries on 2.6.1 are recorded and do not need that call, unless the log says the recording itself failed.
Subscriptions still waiting in requested are counted on careplan.events.subscription.activation.requested. The check runs every 30 seconds (fire-arrow.careplan-events.cleanup.activation-backlog-check-interval) and logs a warning while the count is not zero. In a cluster the check runs on the leader. Each finished materialization is counted on careplan.events.materialization.outcome.
Materialization ran on the caller's request
Creating Tasks for many CarePlans at once filled the shared background queue. Further plans were then materialized on the thread that had just saved the CarePlan, or on the thread delivering subscription matches. Those requests slowed down for as long as the wave lasted.
Starting with 2.6.1, materialization uses its own pool, fire-arrow.careplan-events.async.materialization-core-pool-size (default 4). Set that value below the database connection pool maximum so searches and other writes keep connections while CarePlans are materializing. Idle workers stop when nothing is materializing. In a cluster, each instance uses its own pool. The reconcile sweeper still runs on the leader only.
A concurrent update of the same CarePlan can also lose the write that records which version has been materialized. That write is now retried up to three times. If it still cannot be stored, Tasks that were already written stay written, and the reconcile sweeper completes the record.
A timezone valueString left Tasks requested
The timezone extension http://hl7.org/fhir/StructureDefinition/timezone on a CarePlan, or on its Patient when the CarePlan has none, decides whether due Tasks move from requested to ready. Only valueCode was read. A client that stored the same IANA id in valueString — for example Europe/Berlin — was treated as having no timezone. The plan stayed on local time only, and its Tasks stayed requested.
Starting with 2.6.1, valueString is accepted on both the CarePlan and the Patient. PlanDefinition/$apply still writes valueCode. A value that is not a string, is blank, or is not a valid timezone id is still ignored. No configuration change is required.
CarePlan comparison logged containment warnings
Saving a CarePlan compares the new content with the stored plan to see whether scheduling changed. That comparison logged Resource has a reference that begins with '#', but no resource with this ID is contained for references inside a contained RequestGroup, including the shape PlanDefinition/$apply writes. The reference resolves against the full CarePlan. The comparison result was already correct, and the warning did not change what was stored.
Starting with 2.6.1, that comparison does not log the warning. Validation of a request a client submits is unchanged.