Security

Security notice

Every statement on this page describes a control that is implemented in the system as it stands today. Where something is planned but not built, it is not on this page. Where something is true but incomplete, the limit is stated.

Last reviewed: 10 September 2026

Authentication

Sign-in is by email address and password. There is no public registration: accounts are created by the operator, and self-service sign-up is disabled at the authentication service, so an unknown person cannot create an account.

  • Passwords are stored only as hashes by the authentication service.
  • New passwords are checked against the Have I Been Pwned breach corpus and rejected if they appear in it.
  • A signed-in user must supply their current password to change it. A password reset by email link is exempt, as the link itself is the proof.
  • An account issued with a starting password is held on a change-password screen and cannot reach any case data until that password has been replaced.
  • The password-reset form gives the same response whether or not the address is registered, so it cannot be used to discover who holds an account.
  • Repeated failed sign-in attempts from the same browser are throttled by the sign-in screen, and the authentication service applies its own rate limits.

Sessions expire after a period of inactivity: an idle signed-in session is signed out automatically after 30 minutes without interaction, and the cached data in the browser is discarded with it. Sign-out is available on every signed-in screen. Signing out clears the session token and the cached case data, and the session cannot be resumed with the browser back button.

[TO CONFIRM] Whether the client requires multi-factor authentication or single sign-on. Neither is implemented today.

Access control is enforced in the database

Authorisation does not depend on the interface. Every table carries row-level security, and the browser holds only a publishable key that grants no privileges of its own — a request is evaluated against the signed-in user's role and scope by the database itself. A user who bypasses the interface entirely and calls the data API directly is subject to the same policies.

A user's role is held in a separate profile record, not on the account they control:

  • No role has permission to write to a profile through the application. A user cannot change their own role, their own vessel scope, or anyone else's. Roles are set by the operator server-side.
  • Role checks in policies run through fixed database functions rather than a value the client supplies.

What a vessel operator can and cannot read

Vessel operators have no read access to the findings or readings tables. There is no policy granting it, so a direct query returns zero rows regardless of what is asked for. They also have no access to the reviewer decision records, to engine run metadata, or to the rule base.

At the client's request, vessel operators do see the results of the review for their own case, through a single restricted database view. That view exposes the parameter, its domain, the rule statement, the disposition, the traffic light and the severity — and nothing else. Supporting evidence, reviewer notes, comparison detail, rule identifiers and rule base versions are not in it, and the current run only is exposed. Access to the view is limited to cases whose carrier is in that operator's own vessel scope.

Separation between clients

Case data is visible only to users with a legitimate scope over it. A vessel operator sees only cases whose carrier name appears in their own vessel scope — their case list, their documents, their clarifications and their results are filtered by that scope in the database. One operator cannot read, upload against, or discover another operator's case. This is enforced by policy on every table concerned, and has been verified by signing in as a scoped account and querying for a case outside its scope.

The limit worth stating plainly: the reviewing party's own roles — marine superintendent and MOL administrator — can see every case in this deployment. That is the operating model of a single reviewing organisation, not a multi-tenant separation between reviewing organisations. [TO CONFIRM] Whether a second reviewing organisation will ever share this deployment. If so, per-client separation for MOL roles would need to be designed; it does not exist today.

Review decisions cannot be edited or deleted

Reviewer actions, reading confirmations and case status changes are append-only. The database grants no update or delete permission on them to any application role — not to a vessel operator, not to a superintendent, not to an administrator. There is no interface control to edit or remove one, and adding one would not work.

Clarifications are similar but narrower: the question, who raised it and when are immutable, a released question cannot be withdrawn, and once an answer is recorded it cannot be changed. This is enforced by a database trigger, not by the interface.

Cases and documents cannot be deleted through the application either; a superseded document version is linked forward and retained rather than overwritten.

Uploaded documents

Submitted documents are held in a private object storage bucket. The bucket is not public: no document has a permanent public URL, and an unauthenticated request for one fails. Access is by short-lived signed link, generated on demand for a user whose role and scope already permit the case.

Uploads are never overwritten. A resubmission is stored as a new version at a new path, and the earlier version is retained and linked. The recorded format of a file is determined from its leading bytes, and stored separately from the format the browser declared, so a mismatch between the two is visible rather than resolved silently.

The rule engine

Compatibility findings and readings are produced outside the application by the rule engine, and written using a service role credential. No application role can insert, update or delete a finding, a reading, a parameter, a rule or an engine run — they are read-only to every signed-in user, including administrators.

The service role key exists only as a server-side environment variable. It is not present in the source code, not in any client-visible environment variable, and not in anything shipped to a browser. The browser holds only the publishable key, which is subject to row-level security.

No compliance result is computed in the application, in a server function or in a database trigger. The application counts and displays stored rows; it does not decide.

Transport and infrastructure

All traffic between the browser, the application and the database endpoint is over HTTPS. The database, authentication and storage are a managed Supabase service on Amazon Web Services infrastructure.

[TO CONFIRM] The client's requirements on backup frequency and retention, restore testing, penetration testing, breach notification timescales, and any certification (ISO 27001, SOC 2) they expect from the hosting arrangement.

Reporting a vulnerability

Report a suspected vulnerability to the operator before disclosing it elsewhere. [TO CONFIRM] The security contact address and the response commitment.