Trust centre
Last reviewed: 23 September 2026
Overview
Learning Platform is a corporate learning platform for client organisations, built and operated by Michael Shreeves as an individual, not a company. See about for what it does and who it's for.
Data protection
What's collected, why, the lawful basis and how long it's kept are set out in full in the privacy notice rather than repeated here, including who is the controller for which category of data - an organisation is the controller for its own learners' data, and Learning Platform acts as its processor.
Account deletion is self-service; data export is not. A person deletes their own account from the account menu: in an organisation that removes them at once, restorable by an organisation admin, who can also erase the account (name and email replaced with placeholders, the audit trail kept as “Former user”, training records kept as the organisation's). A request for a copy or a correction is still handled manually - see the privacy notice's contact address.
Security controls
-
Authentication. Symfony's own security
component; passwords are hashed with the platform's strongest
available algorithm (Symfony's
autohasher, which resolves to bcrypt or Argon2i depending on what the PHP build supports) — never stored or logged in plain text. -
Two-factor authentication (MFA). TOTP
(authenticator app), via
scheb/2fa-bundle. Mandatory, not merely offered, for every organisation administrator and platform administrator — an admin with no TOTP configured is redirected to set it up on every request until they do (TwoFactorEnforcementSubscriber), rather than being trusted to opt in. Optional for a learner or a course author. A database table for WebAuthn/passkey credentials already exists, but the enrolment flow for it is not built yet — TOTP is the only working second factor today. - Secrets at rest. TOTP secrets are encrypted in the database (libsodium secretbox) with a key held in the server's environment, not in the database or the repository. An organisation's Microsoft sign-in client secret is encrypted the same way by the shared sign-in package, under its own derived key.
-
Microsoft sign-in (optional). Anyone with a
Microsoft work or school account can sign in through a shared
Microsoft registration, and an organisation can connect its own
Entra registration, require it instead of passwords, add people
automatically (off by default, optionally restricted to a group,
as learners with no administration rights), and have leavers
deactivated by a nightly group sync. Authorisation-code flow with
PKCE; the email claim is used only when Microsoft verifies it;
personal Microsoft accounts are refused. The platform's own
second factor is skipped when Microsoft reports that it performed
MFA (the ID token's
amrincludesmfa), through an organisation's own registration or the shared one and for every role. An organisation admin can require the platform's own factor for a particular person even after Microsoft MFA - their setting, not the person's, and every change is audited. A deactivated account can't sign in by any route, its remembered sessions are revoked and an open session ends on its next request; its training record is kept. Every settings change and every account created, linked, deactivated or reactivated is written to an append-only audit record with before and after values. -
Authorisation. A short role hierarchy
(learner → author → organisation admin →
platform admin) governs broad capability; per-object decisions
are checked against the actual entity by a voter
(
CourseVoter,CertificateVoter,AssessmentAttemptVoter) rather than the role alone. -
Tenancy. Every query against organisation-owned
data is scoped by organisation through a Doctrine filter
(
OrganisationScopeFilter), enabled on every web request and parameterised to the signed-in user's own organisation — not trusted from a controller or a request parameter. The one deliberate exception is a platform administrator, who belongs to no tenant and needs to see across all of them. -
Response security headers, set on every
response outside local debugging: a Content Security Policy
(script execution restricted to a per-request nonce, no
arbitrary inline script),
X-Content-Type-Options: nosniff,X-Frame-Options: DENY,Referrer-Policy: strict-origin-when-cross-origin, a locked-downPermissions-Policy, andStrict-Transport-Securityon every HTTPS response. - Encryption in transit. HSTS is sent on every secure response, which assumes HTTPS is terminated in front of the application — and it is: Cloudflare sits in front of learning-platform.mashreeves.com and handles HTTPS for every request.
- Encryption at rest. Depends on the hosting provider, Vultr — see Availability below. The application adds no encryption of its own on top of what the host provides; passwords and single-use tokens are hashed, not encrypted, because the application never needs to read them back.
- No third-party script, font or embed on any page. The typeface (Inter) is self-hosted, not loaded from Google Fonts or any other CDN, so visiting a page — signed in or not — tells no third party that you did, beyond Cloudflare in front of the site itself (see Subprocessors).
-
Secrets. Never committed —
.envin this repository ships with every secret blank and a comment saying to set it in an untracked.env.local.
Development practices
- Static analysis at PHPStan level 8, no baseline suppressions used to get a green run.
- Coding standards enforced by PHP CS Fixer (PER-CS/PSR-12) on every change, plus Rector checked in the same gate.
- An automated test suite (PHPUnit, with
dama/doctrine-test-bundlerolling back each test's database changes) covering tenancy scoping, authorisation and the mandatory-MFA gate. - All four checks (
composer qa: coding standards, static analysis, Rector, tests) gate what's allowed to merge — a red run is not mergeable.
Subprocessors
| Who | What for | What they see |
|---|---|---|
| Vultr (hosting) | Running the application, database and file storage, in Vultr's Manchester, UK data centre | Everything the application stores |
| Cloudflare (US) | DNS, HTTPS and a proxy in front of every request, under its standard data processing terms | Each visitor's IP address, browser and the page requested |
| Microsoft (Entra ID), only when used | Signing people in with Microsoft; for an organisation that turns it on, reading the membership of one chosen directory group | The person's name, email address and Microsoft account identifiers; the chosen group's members |
| Microsoft 365 (held in the UK) | Delivering set-password, notification and password-reset email. The platform hands each message to a mail server on its own hosting, which passes it to Microsoft 365 to deliver | The recipient's email address and the content of each email |
| Brevo (fallback; France, EU) | Kept as a fallback relay (smtp-relay.brevo.com). It carries email only if the Microsoft 365 route fails and the platform is switched to it |
Nothing day to day. While it is in use, the recipient's email address and the content of each email |
Every page loads cookieless Matomo analytics, self-hosted at
analytics.mashreeves.com on the same Vultr server in Manchester, UK,
and run by Michael Shreeves — so it isn't a subprocessor and
isn't in the table. IP addresses are shortened before they're
stored, and raw visit records are deleted after 13 months. No
advertising or third-party tracking script is loaded on any page.
Live Zoom sessions are part of the product's intended
scope, but the Zoom integration itself is not built yet (see
Lesson.php's own comment) — today, no session
data is exchanged with Zoom or any other video provider.
Availability and resilience
Learning Platform runs at learning-platform.mashreeves.com on a Vultr server in Vultr's Manchester, UK data centre, with Cloudflare in front of it. Each release goes out as an atomic switch to a freshly built release directory, followed by a health check, so a failed deploy doesn't leave a half-updated site. The server is covered by Vultr's automatic backups, and a restore — including a restore to a specific point in time — was tested and confirmed working on 10 September 2026. Uptime isn't formally measured yet, and there's no published availability figure.
Incident response
Report a security concern to hello@mashreeves.com. As a sole individual operator there is no formal incident-response SLA published yet; a report will get a response from a real person, not an automated queue. This section will carry a firmer commitment once Learning Platform has production users depending on one.
Certifications and frameworks
None held by Learning Platform itself. It's a personal project, not operated under an organisation's existing certification. The host is a different matter: according to the hosting account's compliance dashboard, Vultr's Manchester data centre holds ISO 27001, SOC 2, SOC 1 and PCI DSS attestations. Those are Vultr's, covering its infrastructure — not a certification of this application.
Accessibility
Built towards WCAG 2.2 AA as the floor. See the published accessibility statement for what's actually been tested so far, rather than a summary here that could drift from it — today, that's honestly very little, since no formal audit has been carried out yet.
Reporting a vulnerability
Email hello@mashreeves.com with what you found and how to reproduce it. Good-faith security research that doesn't access, modify or destroy other people's data won't be treated as an attack.
The same contact is published in machine-readable form at
/.well-known/security.txt (RFC 9116).
Contact
hello@mashreeves.com for anything on this page, privacy, data rights, or a security concern. For a product question, use support instead.