Changelog
Ce contenu n’est pas encore disponible dans votre langue.
Every release of Open Data Capture from 2.0.0 onward is recorded here. Each section is generated from the commit history when the version changes, and becomes the body of the GitHub release. Releases before 2.0.0 are listed in the release history.
2.6.0
Features
- track the current major release in a generated .env (7dea632)
- choose the time window of each dashboard chart (4354576)
- web: search and sort every dashboard dialog as a plain-text table (8124e2e)
- web: search and sort the dashboard records dialog (8fd2d6a)
- match usernames case-insensitively (045f29e)
- web: make every table column sortable (0148fe6)
- api: order audit and sync logs by any column (98ef1c6)
- web: group every admin link under the Admin Panel (e85f323)
- redirect the subject to a configured URL after a remote assignment is completed (d838ed8)
- react-core: show a progress bar while a series is administered (2d73c31)
- web: manage permission sets and assign them to users (2df4f89)
- api: add permission sets (69c7ae3)
- add an instance-wide table color mode and tag more table columns (0444a7d)
- web: show status in tables as colour tags instead of tinted text (834ad1b)
- web: show instrument repositories and their activity log on separate tabs (37a0ab8)
- let administrators clear the instrument repository sync history (324669a)
- record instrument repository syncs and the instruments that fail them (c3c5dd3)
- web: send bulk remote assignment links by email (af79f2e)
- api: carry every link for a subject in one assignment email (b0d1542)
- schemas: add a bulk assignment email contract (d020491)
- react-core: summarize form validation errors under the submit button (7a31537)
- add limit to date of birth to today (bcb18a8)
- web: improve datahub filters, navigation, and chart exports (f6d3f82)
- add the instrument hub and record provenance (17df2ce)
- web: move the datahub behind a subject hub (d4bf375)
- api: summarize instrument records by subject (66c193b)
- web: warn before leaving an instrument whose responses are unsaved (82ed5d4)
- add an admin instruments page, and let administrators archive series (d7147b0)
- api: document request bodies in the API reference (6cc330e)
- web: save a user’s account and permissions with one button (6c80efc)
- record archiving as an archivedAt date, separate from disabled (dfa4b3a)
- add unarchive, status column, and archived login message (1b137bd)
- replace delete user with archive (soft-disable) (623b204)
- add bulk delete assignments with sidebar navigation (e2e30f0)
- subjectdemographics subtype of subject schema (0f9e67d)
- new query to get custom id subjects dob and sex (842857c)
- make it so that dob and sex are locked if custom subject already has one in session form (5228aa8)
- adjust backend to deal with default group subjects (4afd9ae)
- adjust custom id query to find default group (ed5a892)
- update backend to allow admins to see groupless custom ids (ec752b8)
- adjust query to allow admins to see groupless custom ids (5e33d48)
- web: let admins switch to any group (9ba1e9b)
- web: log in from a click anywhere on a demo user’s row (2bd627a)
- web: brand the demo login dialog with the Open Data Capture logo (b7f6cad)
- react-core: add copy-to-clipboard button to error page (b41eb4e)
- add search submit guard to datahub page (e80d563)
- add search submit guard stopping page from reloading (66d794d)
- web: open the demo login information over the login page (4015e97)
- gateway: serve a landing page on the gateway root (cdba51f)
- add subjects backend api call to return custom ids (1e24295)
- add new useSubjectCustomIdsQuery mutation and test file (4340bf1)
- add custom ids type to subject object file (cbc5759)
- change custom ids query to only return the ids (15ddb88)
- add return functionality to manage user form (db5f6ac)
- web: show when each instrument was added, and line up the rows (687d679)
- web: tag each series instrument with its creation date (538f10a)
- translate the remaining interface strings into Spanish (e26833d)
Bug Fixes
- require node 24.22.1 for the october 2026 security releases (18c1414)
- api: sync a re-added instrument repo with its new token (4005c6b)
- api: refuse re-adding an instrument repo the caller cannot update (d5472af)
- web: hide file instruments from remote assignment (14856b1)
- api: refuse remote assignment of file instruments (2b1b6f6)
- api: scope instrument record updates by update rules (d14bad9)
- count every instrument edition on the dashboard (6711523)
- web: accept record fields in CSV upload (3b68f72)
- web: accept literal, literal union and native enum fields in CSV upload (f794197)
- web: name the column and Zod type when a CSV upload field is unsupported (622efe6)
- web: keep a nav group open on pages beneath its links (a8c880a)
- web: separate the send email tag colour and rename colour mode labels (3d503b8)
- playground: stop matching instrument ids in the selector search (5219572)
- web: add accessible legend to dashboard trends (3cc4e0c)
- playground: search instruments by label (806642e)
- runtime-v1: bump vendored dompurify to 3.4.17 (fadb531)
- runtime-internal: serve every data URL static asset from the worker (b2ea990)
- playground-url: embed SVG files as text in CLI share links (f1ef909)
- report the status for an error response with an empty body (4c3e246)
- playground: submit the JSPsych example’s trials array (eb47f8b)
- outreach: exclude draft blog posts from site and sitemap (44ac21b)
- api: omit assignment encryptionKeyPair from default Prisma selects (6b5640d)
- accept FILE and SERIES in odc-cli instruments list —kind (da75246)
- put @staticmethod above the auth decorators in odc-cli (8ffcbd0)
- web: fit both repository tables to the page at a 1280px viewport (b88a63b)
- address review on the activity log’s schema, ability and test hooks (7a663e6)
- web: hold every activity log cell to one line and widen its repository column (02ceddd)
- correct the activity log’s naming, scoping and presentation (d2cf1e6)
- instrument-guidelines: tell agents to bump the edition of a modified instrument (3dfff3f)
- api: scan for addresses to redact in linear time (cef40dd)
- ignore generated Playwright artifacts (6db417c)
- web: split each record array field on its first colon only (3308f49)
- web: reject a record array cell that is missing its closing parenthesis (63935eb)
- web: offer a retry when every bulk email in the batch failed (fed5a3b)
- web: save the mail configuration when email is enabled (5f9796d)
- send odc-cli’s —min-date with a timezone (1a34ac7)
- schemas: parse dates as ISO 8601 date-times instead of coercing them (1520a9c)
- react-core: focus the first invalid field after a failed submit (7b57715)
- web: align remote assignment tables with the datahub subject table (4db3225)
- web: cap the column mapping table so a wide file cannot push the wizard’s controls off screen (808de17)
- web: sort the active language checkboxes by name (77d3f18)
- web: scope the email send pickers to active languages (a59dbdb)
- web: scope email template languages to active languages (c87ce30)
- schemas: add Spanish to the default email templates (5bab71e)
- web: carry the hub’s charts over to recharts 3 (e23dd15)
- web,api: confine a series lookup to the caller’s groups and derive the timeframe (686c147)
- web,api: address all 10 review issues on datahub instrument hub (ac05548)
- web: confirm before ending the session from the mobile menu (33d220a)
- react-core: report too many files in the dropzone as a count error (dfe11a6)
- web: keep the Add Group form when creating the group fails (d194eb6)
- web: number bulk paste parse errors by the line in the user’s file (d3695b9)
- react-core: show measures declaring the deprecated hidden: false (f38ff7f)
- web: stop the upload instrument list re-rendering while it loads (46764c7)
- instrument-library: show the happiness missing-reason error under its field (1d7fe73)
- instrument-library: correct copy errors in the demographics and happiness forms (bc96756)
- instrument-library: accept a lowercase postal code and an age at immigration of 0 (67337e0)
- instrument-bundler: resolve subpaths of scoped runtime packages to .js (fef4bcf)
- instrument-bundler: stop reporting an internal TypeError on failed builds (7143a28)
- playground: load the default template after deleting an instrument (2daf0ce)
- playground: keep the active file when a background tab closes (9462a31)
- playground: disable Save for examples and templates (1ac73b8)
- playground: submit a message from the static-assets example (21750a5)
- web: keep handled mutation failures off the route error boundary (b281d60)
- web: stop aborting instrument repository imports after 10 seconds (9945d6d)
- schemas: accept international and grouped national phone numbers (09eba8e)
- web: explain a rejected password in the account page dialog (361ad65)
- web: keep unsaved profile edits when the user query refetches (7bd410a)
- web: ignore the custom logo size while the login image is hidden (ec28908)
- web: fall back through languages for the narrow-screen login heading (e5ff0ca)
- web: prefetch audit logs under the key the table reads (9b1bae0)
- web: sort audit logs oldest first on the first time header click (ee8b928)
- web: stop the About uptime counter running a second ahead (dfd8b7b)
- web: fetch a fresh API uptime when the About page opens (0ff821c)
- web: show only the latest edition of each instrument in the repository view (362b134)
- web: restore the measure-select placeholder for the instrument graph (5657bbc)
- outreach: use the blog title in metadata (17631a5)
- web: preserve instrument showcase tag options (6ac367a)
- outreach: translate the Runtime Core API sidebar label to French (c6f406b)
- outreach: translate docs sidebar section labels to French (c130338)
- testing: update turbo task graph for test:e2e cache (ccb59d9)
- instrument-bundler: bundle files with upper-case extensions (b6f4077)
- runtime-core: preserve snake case keys (5c6e571)
- web: keep the search params when switching datahub tabs (cc70965)
- web: address the instrument hub review (f6054c1)
- api: stop the in-memory replica set when the app shuts down (c33bb42)
- web: reconcile the subject hub with main’s phone-layout and admin-instruments changes (2d22f68)
- web: stop filter controls crowding out search bars on phones (5fc4fd5)
- instrument-utils: leave measures on unrevealed dynamic questions out of the results summary (550aa27)
- api: mask email addresses in mail failure logs and audit entries (f7557d4)
- instrument-utils: read const measures from their ref in the results summary (003c244)
- api: delete record files when deleting records or subjects (2214e7f)
- web: open /admin/instruments without rewriting its address (dd820d8)
- testing: navigate wizard e2e spec to ?mode=create (f753370)
- api: answer the has-record filter for a caller who may read no records (53b54e7)
- web: show one error for an invalid custom identifier (8a69991)
- api: export the records a non-admin may read (e2007a7)
- schemas: keep visibility on computed instrument measures (68417c5)
- api: check the group, instrument and subject a single assignment names (3a601a8)
- let odc-cli read passwords from stdin (74511d2)
- stop proxying /storage to rustfs while storage is disabled (9f24e00)
- publish new installs’ compose ports on loopback only (a220888)
- web: move the user password fixes onto the new user page (06d3da9)
- testing: wait for the instrument rows before measuring their columns (c9a93e0)
- web: stop the strength meter flagging an untouched password field (dfb70a1)
- web: stop saved credentials autofilling the user edit sheet (99a5a09)
- web: cover the _app language reconciliation wiring and address review (014df57)
- address review — upload error fallback, toggle wiring, docs (8167d77)
- scope the complete translation checks (2965fcf)
- add the missing es translations exposed by requireCompleteTranslations (ba7ed1a)
- address PR review — unify gateway language policy, move side-effect to services (f63d623)
- resolve a deactivated language before the app renders (e753efe)
Performance
- api: read instrument bundles only for uncached instances (7a121c0)
- page the activity log instead of loading its whole history (ae42b88)
- api: group the has-record subject ids in the database (4c5a518)
- web: match export rows against a set of listed subject ids (508e7ab)
2.5.2
Bug Fixes
- write odc-cli’s stored token readable by its owner only (8d5e642)
- api: validate a remote submission before creating its session (a5174d7)
- api: file a remote session under the assignment’s own group (5ae4b60)
- api: keep patients’ submissions out of the synchronizer’s logs (24378a7)
- gateway: bound the assignment id the verification route stores (d15a4e9)
- api: check the caller may update an assignment before cancelling it (96cb228)
- api: stop returning an assignment’s private key when it is created (e8a9729)
- generate the .env file readable by its owner only (c32430d)
- api: cap a gateway synchronization pass at five minutes (43b7888)
- api: scope the group and user a new session names to the caller (d6966cf)
- api: delete the sessions a batch inserted when their read-back fails (aeb0f0f)
- api: drop the subject read-back and fail loudly on a session read-back gap (9a40e2c)
- api: stop gateway synchronizations from overlapping (a45c97f)
Performance
- api: batch session creation and scope the upload response (24e3775)
2.5.1
Bug Fixes
- playground: read the preview origin at runtime instead of build time (8022973)
- api: open a new connection for every gateway request (c3f3e79)
- api: refuse the instrument token everywhere but instrument upload (0826634)
2.5.0
Features
- web: drop the empty-state row from the permissions table (f395257)
- web: show a grant scope as plain text, marking every-group grants with an icon (8ea344f)
- web: style the add-permission row like the grants above it (7725b39)
- web: hold the permissions table columns still and match the add control to remove (7da8beb)
- web: put the add-permission controls in the table footer (c284b65)
- web: reorder the user page and give scopes a visible chip (04c2b50)
- web: drop the back link from the user page (fa315ef)
- web: lay the user page out as identity, permissions, account and delete cards (9773f7d)
- web: manage a user and their permissions on a dedicated page (46046b4)
- api: confine additional permissions to a group (8c4cbee)
Bug Fixes
- playground: keep the desktop layout in the preview frame (35e5ef1)
- playground: run instrument previews on a second origin (66c1425)
- api: return only the records an upload wrote, not every group’s (49fd5a9)
- web: stop refetching the instrument bundle when the window regains focus (7fd9025)
- web: truncate a long label in the permissions table selects with an ellipsis (6398fbe)
- api: match uploaded instruments when validating series items (9f435e3)
2.4.0
Features
- playground: surface the API’s own error message when an upload fails (188ad51)
- playground: block bundle upload while the editor reports type errors (5498dca)
Bug Fixes
- playground: match preview mode help tooltip to libui tooltip styling (fa58149)
- playground: replace inner Radix tooltip with CSS-only tooltip in ShareButton (746db5e)
2.3.1
Bug Fixes
- playground-url: carry the share link payload in the URL fragment (a78fdbd)
2.3.0
Features
- adjust loaded permissions with a a useMemo hook to fix initial loading (eabbde8)
- enforce conventional commits and generate the changelog from them (b7073d2)
- web: redesign the audit log filters as a per-facet filter bar (63054fe)
Bug Fixes
- close the three bugs documented by the packages/* coverage pass (1ffd7da)
- administer each item of a skipProgress series as a fresh form (f06b4ff)
2.0.0
Overview
This release introduces file instruments: instruments whose responses are uploaded files (for example single documents or multi-file MRI scan sessions) rather than form data. To support this, Open Data Capture can now use an S3-compatible object storage service. Files are not stored in MongoDB; the browser uploads and downloads them directly via short-lived presigned URLs, and only file metadata is kept in the database.
The default Docker Compose stack now ships a self-hosted RustFS service for this purpose.
This is a non-breaking upgrade. Object storage is opt-in via the new
STORAGE_ENABLEDflag, which defaults tofalse. Existing deployments upgrade and boot unchanged; everything except file instruments works exactly as before. File instruments only become available once you enable storage (see below). WhenSTORAGE_ENABLED=false, attempting to create a file-instrument record returns a clear503 Service Unavailablerather than failing at startup.
What’s new
- File instruments — a new
FILEinstrument kind with one or more file groups, each constrained by an allowed file type and acount(min/max). - Object storage service — a new
rustfsservice in the Compose stack, backed by a./rustfs/datavolume. - Direct browser uploads/downloads — files transfer directly between the browser and the storage service using presigned URLs, keeping large transfers off the API.
- Two demo file instruments (
ARBITRARY_SINGLE_FILE,MRI_SCAN_SESSION) are now seeded by the demo data.
New environment variables
The following variables have been added (see .env.template). “Required” means required only when STORAGE_ENABLED=true; when storage is disabled these are ignored.
| Variable | Required | Description |
|---|---|---|
STORAGE_ENABLED | No | Master switch for object storage / file instruments (default false). When true, the four variables marked “Yes” must all be set or the API refuses to start. |
STORAGE_ENDPOINT | Yes | Internal endpoint the API uses for S3 API calls. In Compose this is http://rustfs:9000. |
STORAGE_PUBLIC_ENDPOINT | No | Externally reachable endpoint embedded in presigned URLs. Defaults to http://localhost:${APP_PORT}/storage, which Caddy proxies to rustfs. Set this to your public storage URL (e.g. https://your-domain.com/storage) for real deployments. |
STORAGE_ACCESS_KEY | Yes | S3 access key. Also used as the RustFS admin access key in the Compose stack. |
STORAGE_SECRET_KEY | Yes | S3 secret key. Also used as the RustFS admin secret key in the Compose stack. |
STORAGE_BUCKET | Yes | Bucket name where files are stored (default open-data-capture). Created automatically on startup if absent. |
STORAGE_REGION | No | Region name. Can be any value for self-hosted S3 (default us-east-1). |
RUSTFS_VERSION | No | RustFS image tag for the Compose stack (default latest). |
Upgrading without enabling storage
If you don’t need file instruments yet, no action is required — pull the new images and restart. STORAGE_ENABLED defaults to false, the bundled rustfs container boots with placeholder credentials but is otherwise unused, and the rest of the application is unchanged.
To enable file instruments later, follow the steps below.
Enabling object storage
1. Add the storage configuration
Set STORAGE_ENABLED=true and provide the storage credentials. If you generate your .env with scripts/generate-env.sh, re-run it — it now generates STORAGE_ACCESS_KEY and STORAGE_SECRET_KEY for you. Back up your existing .env first, since regenerating will also produce new values for other secrets.
Otherwise, copy the new STORAGE_* entries from .env.template into your existing .env and fill them in. At minimum:
STORAGE_ENABLED=trueSTORAGE_ENDPOINT=http://rustfs:9000STORAGE_PUBLIC_ENDPOINT=STORAGE_ACCESS_KEY=$(openssl rand -hex 16)STORAGE_SECRET_KEY=$(openssl rand -hex 32)STORAGE_BUCKET=open-data-captureSTORAGE_REGION=us-east-1When STORAGE_ENABLED=true, all of STORAGE_ACCESS_KEY, STORAGE_SECRET_KEY, STORAGE_BUCKET and STORAGE_ENDPOINT must be set or the API will refuse to start. Leave STORAGE_PUBLIC_ENDPOINT blank to use the Caddy-proxied default (http://localhost:${APP_PORT}/storage). Set it explicitly to your externally accessible storage URL for a production deployment behind a custom domain.
2. Update your reverse proxy
The bundled Caddyfile now proxies /storage/* to the storage service so the browser can reach presigned URLs:
handle_path /storage/* { reverse_proxy rustfs:9000 { header_up Host rustfs:9000 }}If you use the bundled Caddy configuration, pull the updated Caddyfile. If you run your own reverse proxy, add an equivalent route forwarding /storage/* to the storage service on port 9000, and ensure the upstream Host header matches the storage host. Otherwise, set STORAGE_PUBLIC_ENDPOINT to a URL your users’ browsers can reach directly.
3. Pull the updated Compose stack and restart
The updated docker-compose.yaml adds the rustfs service (with a ./rustfs/data bind mount) and wires the new variables into the api service. Make sure ./rustfs/data is on persistent, backed-up storage.
docker compose pulldocker compose up -dOn first boot the API creates the STORAGE_BUCKET automatically if it does not already exist.
Backups
File contents now live in object storage, not in MongoDB. Update your backup strategy to include the storage volume (./rustfs/data for the bundled stack, or your S3 bucket) alongside your existing database backups. A database backup alone will no longer capture uploaded files.
Notes
- File uploads and downloads go directly from the browser to the storage service via presigned URLs, so the storage
STORAGE_PUBLIC_ENDPOINTmust be reachable from end users’ browsers, not just from the API container. - If you supply your own managed S3 (e.g. AWS S3, MinIO) instead of the bundled RustFS, point
STORAGE_ENDPOINT/STORAGE_PUBLIC_ENDPOINTat it, set the matching credentials and region, and you can drop therustfsservice.