Skip to content
PartialFor IT security, vendor qualification, sponsors, CROs

Security and hosting

Last updated Oct 21, 2026

What this page is

What is built into the software, and what depends on how you run it. No certification is claimed. How the parts are deployed is on Running it; the data's separation is on Data and tenancy.

Identity and sessions

  • No passwords. Sign-in is a six-digit code sent to a registered address: valid five minutes, three attempts, a 60-second cooldown before another, no more than three codes for one account from one address in 15 minutes. Codes are stored only as an Argon2id hash. Repeated wrong codes pause sign-in in that browser window for fifteen minutes and are recorded; an account is never locked by someone else's guessing (Signing in).
  • No account enumeration. A stranger's request for a code is answered exactly as a request for a real account is (a challenge, the same words, the same cooldown); only the audit ledger records why no code was sent.
  • The browser holds a sealed cookie, never a token it can read. The web application keeps the access and refresh tokens inside an encrypted, httpOnly, sameSite=lax cookie (secure in production), split into chunks when it is long, and calls the API on the person's behalf. Script on the page cannot read it.
  • Session lifetimes. 15 minutes idle and 12 hours absolute; an access token lasts 15 minutes and a refresh token 7 days. A person can list and end their own sessions (Manage your settings and sessions).
  • Refresh with reuse detection. A refresh rotates the token; presenting an already-used one ends the session.
  • A person holds one role on a study, the same in UAT and in Live, and the roles on different studies are never merged: what they may do is decided by their role on that study, in the workspace they are in.
  • No single sign-on and no API keys. There is one way in, the code (API and integrations).

Authorization

  • Fails closed. A screen that is not registered is unreachable, not merely ungated.
  • Checked on the server. Hiding a button is presentation; the refusal is made by the API, so a script cannot do what a screen refuses (API reference).
  • Separation of duties is held in the role editor and again at the moment of the act: the person who raised a query cannot answer it, three different people sign a release, the lock needs two, a person cannot sign their own delegation (Who can and cannot).
  • No blanket administrator override. The clinical and signing operations use a strict check with no administrator bypass; the role editor's hard guardrails are refused for an administrator as for anyone.
  • Not found, never forbidden. A study the caller is not a member of answers 404, so the system does not confirm that data it will not show exists.
  • Refusals are audited as access decisions.

Tenant isolation and the workspace wall

Organizations share one deployment and are separated in the database: 66 of the 72 tables carry an organization id and a row-level security policy keyed to the organization on the connection, which the application sets from the authenticated session on every checkout and clears on release. With no tenant set, a query matches zero rows. A second wall separates a study's UAT data from its Live data on the clinical tables. The application connects as a least-privilege role that does not own the tables, cannot change a ledger, and is checked at boot: in production a deployment that got this wrong refuses to start (Data and tenancy, The database).

Application hardening

  • Headers. The API sends Helmet's headers (X-Content-Type-Options, Referrer-Policy: no-referrer, a same-site cross-origin resource policy) and strict transport security in production for two years with subdomains and preload. The web application adds X-Frame-Options: DENY, nosniff, a strict referrer policy, a permissions policy that turns off camera, microphone and location, strict transport security, and a content-security-policy with a nonce on every page, set for each request.
  • CORS is locked to the configured origins.
  • Rate limits, counted by the viewer's address as the web application vouches for it: 600 requests a minute for ordinary routes and 30 for sign-in routes by default, and a production check refuses an authentication limit over 100.
  • Validation. Every request body, query and path parameter is checked against a schema before it reaches business logic, and a request body over 2 MB is refused.
  • Errors use one envelope; in production the message of an unexpected error is replaced by a generic one.
  • Logs are structured and correlated by request id, and redact the authorization and cookie headers and any field named password, token or secret.
  • Shutdown is graceful: the probes flip before connections drain.
  • Configuration is validated at boot, and production and staging refuse unsafe combinations (a development code shortcut, a single database role, a long idle timeout, a placeholder secret, no anchoring of the audit ledger without an explicit waiver) (Running it).

Against the OWASP Top 10

RiskPosition
Broken access controlFail-closed permissions, site confinement, cross-tenant row-level security, the workspace wall, audited refusals
Cryptographic failuresHTTPS with HSTS; codes hashed with Argon2id; SHA-256 fingerprints on signatures, documents and the audit chain; files encrypted at rest in S3
InjectionQueries bind their values as parameters; every request is schema-validated first
Insecure designSeparation of duties, the two-person lock, append-only ledgers, immutable released versions
Security misconfigurationConfiguration validated at boot; production refuses a privileged runtime database role
Vulnerable componentsThe client's and the control plane's release gates fail on a high or critical advisory in production dependencies; the server repository's gate (npm run verify) does not run a dependency audit, so checking its dependencies is an operational task
Identification and authentication failuresPasswordless codes, limited attempts, rotation with reuse detection, short sessions
Software and data integrity failuresThe hash-chained audit trail with anchors, signature and record binding, sealed configuration versions, file checksums
Logging and monitoring failuresA comprehensive audit trail and operator alerts; log shipping, SIEM integration and metrics are yours
Server-side request forgeryThe server makes outbound calls only to destinations that are configured (the control plane, storage, the mail relay, the AI provider), never to an address a user supplies

What depends on your deployment

These are real controls that the application cannot provide:

ControlOwner
Database encryption at restYour database provisioning
Backup, restore testing, disaster recoveryYour infrastructure (the tooling and the stale-backup alert are provided)
Retention periods and archivalYour policy and infrastructure
TLS certificates, key management and rotationYour hosting and key service
Network segmentation, WAF, DDoS protectionYour network
Intrusion detection, SIEM, alert routing (ALERT_EMAILS)Your security operations
Penetration testing and code security reviewYour programme
Physical securityYour hosting provider
Data residencyYour region choices
Patching of the host and the databaseYour operations
Secrets managementYour secret store

Operational maturity, stated plainly

Audit events, mail, exports and the dashboards' summary tables are carried by database tables and the worker, so they survive a restart. What does not: notifications and the in-process event bus. The API's rate-limit counters are in the process's memory, so they are per process; there is one API process and one worker, a deploy is in place with a brief outage, and the generic job queue is an unused stub. No metrics are emitted and there is no distributed tracing. The probes, alerts and logs are what exist (Running it). For a pilot this is workable; for a production trial at scale, plan external observability and redundancy.

No certification is claimed

Trialion is not certified against ISO 27001 or SOC 2. The controls above map onto many of their requirements (access control, cryptography, logging, change management, segregation of duties), but a certification covers an organization's management system, not a piece of software. If you need one, it is the hosting and operating organization that would be certified.

Compliance Q&A

Compliance Q&ABuiltAnnex 11 12Part 11 11.10(d)GDPR art.32
Is access protected against unauthorized use at the network edge, with transport security and secure session handling?

Yes for what the application controls: transport security headers, a content-security policy with a nonce, a sealed httpOnly session cookie, rate limits and schema validation on every request, and production refusing unsafe configuration.

Limits: certificates, the network boundary and the protection of the host are the hosting's.

Evidence: server api/middleware/security.ts and rate-limit.tsclient next.config.ts, proxy.ts, lib/bff/session.tsconfig production invariants

Compliance Q&APartly builtAnnex 11 13GDPR art.33GDPR art.32
Are security incidents detected and reported so that they can be assessed?

In part. The worker's one-minute sweep raises an alert by e-mail to the configured recipients when the audit ledger no longer verifies, events are not being recorded, mail is not leaving, a backup is stale or exports stall, and clears it when the condition recovers; refusals and sign-in failures are in the audit trail.

Limits: an alert with no recipient only logs, so setting ALERT_EMAILS is part of going live. There is no intrusion detection or SIEM feed, successful reads are not recorded, and the incident procedure (who assesses, who reports, how fast) is the organization's.

Evidence: server/src/infrastructure/alerting (audit.chain_integrity, audit.anchor, audit.partitions, audit.outbox, mail.delivery, mail.relay_idle, summary.lag, summary.drift, backup.stale, exports.stalled)alert e-mail to ALERT_EMAILS

Compliance Q&APartly builtAnnex 11 7ALCOA+ enduringALCOA+ available
Is data held safely, and can it be restored, in the way Annex 11 expects of storage?

In part. Nightly logical dumps to versioned storage, a weekly restore verification that also checks the audit chain in the restored copy, a record of each run, and an alert when a backup is more than 26 hours old or a verified restore more than 8 days old.

Limits: the application cannot schedule these itself: the jobs must be installed, the storage and the database's own backups configured, and the recovery rehearsed by the operator.

Evidence: scripts/backup (pg-backup, pg-verify-restore)backup_runsworker alert backup.staledocs/operations/backup-and-recovery.md

Data privacy · Audit trail and evidence · Running it · Technical overview