Skip to content

feat(core): add FILE instrument kind and storage schemas#1379

Merged
joshunrau merged 9 commits into
DouglasNeuroInformatics:mainfrom
joshunrau:v2_final
Jun 11, 2026
Merged

feat(core): add FILE instrument kind and storage schemas#1379
joshunrau merged 9 commits into
DouglasNeuroInformatics:mainfrom
joshunrau:v2_final

Conversation

@joshunrau

@joshunrau joshunrau commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator

A summary of the file-instrument feature: a new instrument kind that lets
subjects/users upload arbitrary files, with the files stored in an S3-compatible
object store (RustFS) rather than in the database. Covers the schema/type
foundation, the API storage service and upload/download endpoints, the web +
react-core upload/download UI, example instruments and tooling, the deployment
stack, and docs.

NOT TECHNICALLY BREAKING CHANGE BUT RELEASED UNDER V2 BECAUSE PEOPLE SHOULD UDPATE DOCKER COMPOSE STACK.

Core types & schemas

feat(core): add FILE instrument kind and storage schemas

  • Added a new FILE instrument kind alongside the existing kinds, with its own
    runtime type (runtime-core/src/types/instrument.file.ts) and validation
    schema (schemas/src/instrument/instrument.file.ts).
  • Wired the new kind through the shared base/core instrument types and schemas
    (instrument.base, instrument.core, instrument.interactive,
    instrument.ts) and the runtime define/i18n/constants entry points.
  • Added storage schemas (schemas/src/storage/storage.ts) describing stored-file
    metadata, and extended instrument-records schemas to carry file references.
  • Added a type guard and translation handling for file instruments in
    instrument-utils (guards.ts, translate.ts).

API — object storage & file endpoints

feat(api): add object storage service and file upload endpoints

  • New StorageModule / StorageService providing an S3-compatible object-store
    client (apps/api/src/storage/).
  • New file upload/download surface under instrument-records
    (instrument-records/files/): controller, service, and types for uploading
    and retrieving record files.
  • Prisma schema updated to persist file references on instrument records.
  • Auth/ability changes so file upload/download is gated by the existing
    permission model (ability.factory.ts, ability.utils.ts, auth.types.ts,
    plus added ability tests).
  • Environment schema extended with object-storage configuration
    (core/schemas/env.schema.ts); demo service seeds accordingly.

Web + react-core — upload/download UI

feat(web): add file instrument upload and download UI

  • New FileInstrumentContent component family in react-core
    (Dropzone, UploadProgressBar, ErrorBox, a state store, and types) for
    selecting, validating, and uploading files with progress.
  • InstrumentRenderer / ScalarInstrumentRenderer / series renderers extended
    to render the file-instrument content and report results.
  • Added a NavigationBlocker + NavigationBlockerDialog to warn against
    navigating away mid-upload.
  • Datahub record view reworked: record route moved under
    datahub/$subjectId/table/$recordId with file download support; new
    useInstrumentRecordFilesQuery and an updated
    useUploadInstrumentRecordsMutation (multipart/progress).
  • Axios service updated to support upload progress; datahub translations added.

Instruments — examples, stubs & serving

feat(instruments): add file instrument examples and stub/serve support

  • Example file instruments in instrument-library:
    ARBITRARY_SINGLE_FILE and MRI_SCAN_SESSION.
  • File-instrument stub (instrument-stubs/src/file.js) and interactive stub
    updates.
  • serve-instrument (CLI, root, server) extended to bundle and serve file
    instruments.

Deployment & configuration

chore: add RustFS object storage to the deployment stack
chore(deps): update lockfile and workspace config for file instruments
make storage optional

  • Added a RustFS object-storage service to docker-compose.yaml, with Caddy
    routing, Dockerfile updates, .env.template keys, and generate-env.sh
    support.
  • Updated pnpm-workspace.yaml, turbo.json, root/gateway package.json, and
    the lockfile for the new dependencies.
  • Made object storage optional: the API now starts without storage
    configured (env schema, StorageModule/StorageService, and
    instrument-records service degrade gracefully when storage is absent).

Docs

docs: add v2.0.0 migration guide for object storage

  • Added docs/en/6-updating/v2.0.0.md describing how to provision/configure
    object storage when upgrading to v2.0.0.

Summary by CodeRabbit

Release Notes v2.0.0

  • New Features

    • Added file instrument support for collecting file uploads from subjects
    • Introduced optional S3-compatible object storage configuration for file management
    • Added browser-direct file downloads via presigned URLs with progress tracking
    • Enhanced interactive instruments with language selection and locking features
    • Added navigation blocking to prevent accidental data loss during uploads
  • Infrastructure

    • Updated Docker configurations with pinned dependencies
    • Added RustFS service for optional file storage backend

joshunrau and others added 9 commits June 11, 2026 16:08
Introduce the FILE instrument kind in runtime-core (file-type constants,
FileInstrument types with per-group basename/type/count) and the matching
zod schemas in @opendatacapture/schemas, including file location/metadata,
presigned URL info, and the upload-complete payload. Add the `pending`
flag and file metadata to instrument records, and extend instrument-utils
guards/translation helpers for the new kind.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a StorageService backed by an S3-compatible client (presigned upload
and download URLs, bucket bootstrap on init) and a FilesService/controller
exposing list, presigned upload-url, and upload-complete routes scoped to
an instrument record. Records for FILE instruments are created in a
`pending` state and excluded from results until uploads complete. Wire up
the new InstrumentRecordFile auth subject and abilities, add the storage
env vars, register the storage module, and seed file instruments in demo
data.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add the FileInstrumentContent components (dropzone, upload progress, error
box, upload store) and a NavigationBlockerDialog to guard against leaving
mid-upload, and render them through the instrument renderer. On the web
app, wire presigned upload/download via useInstrumentRecordFilesQuery and
the upload mutation, add a per-record file view under the datahub table
route, and allow opting out of the default request timeout for long
uploads.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add ARBITRARY_SINGLE_FILE and MRI_SCAN_SESSION example instruments to the
library (and register them in the availability script), a file instrument
stub, and update serve-instrument to render and serve the FILE kind.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a RustFS (S3-compatible) service to docker-compose with a persistent
data volume, proxy /storage/* to it via Caddy, and generate storage
access/secret keys in generate-env.sh. Document the new STORAGE_* and
RUSTFS_VERSION variables in .env.template, ignore the rustfs data dir, and
update the app Dockerfiles.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add the new storage/upload dependencies, refresh the pnpm lockfile and
workspace catalog, update turbo pipeline config, and bump the pnpm
packageManager version.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Document the new S3-compatible storage requirement, STORAGE_* env vars,
Caddy/reverse-proxy and docker-compose changes, and backup implications
for upgrading deployments.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@joshunrau joshunrau merged commit de4b788 into DouglasNeuroInformatics:main Jun 11, 2026
1 of 2 checks passed
@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: f7d31f67-bc56-4677-b5a8-207130362350

📥 Commits

Reviewing files that changed from the base of the PR and between 2900c93 and 1a08cf7.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (105)
  • .env.template
  • .gitignore
  • Caddyfile
  • apps/api/Dockerfile
  • apps/api/package.json
  • apps/api/prisma/schema.prisma
  • apps/api/src/auth/__tests__/ability.factory.test.ts
  • apps/api/src/auth/__tests__/ability.utils.test.ts
  • apps/api/src/auth/ability.factory.ts
  • apps/api/src/auth/ability.utils.ts
  • apps/api/src/auth/auth.types.ts
  • apps/api/src/core/schemas/env.schema.ts
  • apps/api/src/demo/demo.service.ts
  • apps/api/src/instrument-records/__tests__/instrument-records.service.spec.ts
  • apps/api/src/instrument-records/files/files.controller.ts
  • apps/api/src/instrument-records/files/files.service.ts
  • apps/api/src/instrument-records/files/files.types.ts
  • apps/api/src/instrument-records/instrument-records.module.ts
  • apps/api/src/instrument-records/instrument-records.service.ts
  • apps/api/src/main.ts
  • apps/api/src/storage/storage.module.ts
  • apps/api/src/storage/storage.service.ts
  • apps/gateway/Dockerfile
  • apps/gateway/package.json
  • apps/playground/Dockerfile
  • apps/web/Dockerfile
  • apps/web/package.json
  • apps/web/src/components/NavigationBlocker.tsx
  • apps/web/src/hooks/useCreateSetupStateMutation.ts
  • apps/web/src/hooks/useInstrumentRecordFilesQuery.ts
  • apps/web/src/hooks/useUploadInstrumentRecordsMutation.ts
  • apps/web/src/route-tree.ts
  • apps/web/src/routes/_app/datahub/$subjectId/$recordId.tsx
  • apps/web/src/routes/_app/datahub/$subjectId/route.tsx
  • apps/web/src/routes/_app/datahub/$subjectId/table/$recordId.tsx
  • apps/web/src/routes/_app/datahub/$subjectId/table/index.tsx
  • apps/web/src/routes/_app/datahub/index.tsx
  • apps/web/src/routes/_app/instruments/render/$id.tsx
  • apps/web/src/routes/_app/user.tsx
  • apps/web/src/services/axios.ts
  • apps/web/src/translations/datahub.json
  • docker-compose.yaml
  • docs/en/6-updating/v1.7.0.md
  • docs/en/6-updating/v2.0.0.md
  • package.json
  • packages/instrument-bundler/package.json
  • packages/instrument-guidelines/package.json
  • packages/instrument-library/package.json
  • packages/instrument-library/scripts/available.ts
  • packages/instrument-library/src/file/ARBITRARY_SINGLE_FILE/index.ts
  • packages/instrument-library/src/file/MRI_SCAN_SESSION/index.ts
  • packages/instrument-stubs/package.json
  • packages/instrument-stubs/src/file.js
  • packages/instrument-stubs/src/interactive.js
  • packages/instrument-utils/src/guards.ts
  • packages/instrument-utils/src/translate.ts
  • packages/react-core/package.json
  • packages/react-core/src/components/FileInstrumentContent/Dropzone.tsx
  • packages/react-core/src/components/FileInstrumentContent/ErrorBox.tsx
  • packages/react-core/src/components/FileInstrumentContent/FileInstrumentContent.tsx
  • packages/react-core/src/components/FileInstrumentContent/UploadProgressBar.tsx
  • packages/react-core/src/components/FileInstrumentContent/index.ts
  • packages/react-core/src/components/FileInstrumentContent/store.ts
  • packages/react-core/src/components/FileInstrumentContent/types.ts
  • packages/react-core/src/components/FormContent/FormContent.tsx
  • packages/react-core/src/components/InstrumentIcon/InstrumentIcon.tsx
  • packages/react-core/src/components/InstrumentRenderer/InstrumentRenderer.stories.tsx
  • packages/react-core/src/components/InstrumentRenderer/InstrumentRenderer.tsx
  • packages/react-core/src/components/InstrumentRenderer/ScalarInstrumentRenderer.tsx
  • packages/react-core/src/components/InstrumentRenderer/SeriesInstrumentContent.tsx
  • packages/react-core/src/components/InstrumentRenderer/SeriesInstrumentRenderer.tsx
  • packages/react-core/src/components/InstrumentRenderer/index.ts
  • packages/react-core/src/components/InstrumentRenderer/types.ts
  • packages/react-core/src/components/InteractiveContent/InteractiveContent.stories.tsx
  • packages/react-core/src/components/InteractiveContent/InteractiveContent.tsx
  • packages/react-core/src/components/NavigationBlockerDialog/NavigationBlockerDialog.tsx
  • packages/react-core/src/components/NavigationBlockerDialog/index.ts
  • packages/react-core/src/hooks/useInterpretedInstrument.ts
  • packages/react-core/src/index.ts
  • packages/react-core/src/types.ts
  • packages/runtime-core/package.json
  • packages/runtime-core/src/constants.ts
  • packages/runtime-core/src/define.ts
  • packages/runtime-core/src/i18n.ts
  • packages/runtime-core/src/index.ts
  • packages/runtime-core/src/types/instrument.base.ts
  • packages/runtime-core/src/types/instrument.core.ts
  • packages/runtime-core/src/types/instrument.file.ts
  • packages/runtime-core/src/types/instrument.interactive.ts
  • packages/schemas/package.json
  • packages/schemas/src/instrument-records/instrument-records.ts
  • packages/schemas/src/instrument/instrument.base.ts
  • packages/schemas/src/instrument/instrument.core.ts
  • packages/schemas/src/instrument/instrument.file.ts
  • packages/schemas/src/instrument/instrument.interactive.ts
  • packages/schemas/src/instrument/instrument.ts
  • packages/schemas/src/storage/storage.ts
  • packages/serve-instrument/package.json
  • packages/serve-instrument/src/cli.ts
  • packages/serve-instrument/src/root.tsx
  • packages/serve-instrument/src/server.tsx
  • pnpm-workspace.yaml
  • runtime/v1/package.json
  • scripts/generate-env.sh
  • turbo.json

Walkthrough

Adds file-instrument support end to end: shared types and schemas, auth and persistence, storage-backed upload/download endpoints, web upload and file views, and serve/runtime/package wiring.

Changes

File instruments and storage flow

Layer / File(s) Summary
File instrument types and schemas
packages/runtime-core/*, packages/schemas/*, packages/instrument-utils/*
Adds the FILE kind, file-instrument types, file/storage schemas, and file translation/guard support.
File-record authorization and record persistence
apps/api/src/auth/*, apps/api/src/instrument-records/*, apps/api/prisma/schema.prisma, apps/api/src/demo/demo.service.ts
Adds group-scoped file-record permissions, record/file persistence, file endpoints, and demo file instrument creation.
Storage service and runtime wiring
apps/api/src/storage/*, apps/api/src/core/schemas/env.schema.ts, .env.template, docker-compose.yaml, Caddyfile, scripts/generate-env.sh, apps/*/Dockerfile, package.json, pnpm-workspace.yaml, turbo.json, docs/en/6-updating/v2.0.0.md
Adds storage configuration, RustFS wiring, presigned URL generation, startup/build metadata, and release documentation.
Web upload, download, and file views
apps/web/src/*, packages/react-core/src/components/*, packages/react-core/src/hooks/*, packages/react-core/package.json
Adds file upload rendering, navigation blocking, download handling, file-record pages, route updates, and axios meta-based request behavior.
Serve tool and package distribution
packages/serve-instrument/*, packages/instrument-library/*, packages/instrument-stubs/*, packages/runtime-core/package.json, packages/schemas/package.json, package.json, runtime/v1/package.json, packages/*/package.json
Adds file-instrument serving modes, new instrument library/stub exports, and package version/export updates.

Sequence Diagram(s)

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant