Trust centre
Last reviewed: 23 September 2026
Overview
Signature Tool is an email signature generator, built and operated by Michael Shreeves as an individual, not a company. See about for what it does and who it's for.
What “signature” means here
This is not a document e-signing product. A “signature” in this application is an email signature — the name, job title, logo and contact details rendered as an HTML block for the foot of an email — not an electronic or digital signature applied to a document. There is no signing event to attest to: the builder renders HTML from what you typed and returns it; it does not cryptographically sign, timestamp, witness or notarise anything, and nothing here should be read as making any claim about the legal validity of a document or an agreement. Where the sections below talk about integrity, they mean the integrity of the application — who can change a brand template, whose data is scoped to whom, and what's recorded about an administrator's own actions — not tamper-evidence of a signed document, because no such artefact exists in this product.
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.
Security controls
-
Authentication. Symfony's own authentication 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. Login is throttled (five attempts per 15 minutes, by username and IP together). - Bot protection. Sign-in, registration and password reset each need a passed Cloudflare Turnstile challenge, checked on the server rather than trusted from the browser. If Cloudflare can’t be reached, the request is refused rather than let through.
-
Two-factor authentication (MFA). TOTP (authenticator
app), via
scheb/2fa-bundle. Mandatory, not merely offered, for everyROLE_ADMINandROLE_SUPER_ADMINaccount — enforced regardless of the app-wide MFA toggle, so an administrator with no TOTP confirmed is redirected to set it up before reaching anything else. Optional, at the operator's discretion via that same toggle, for an ordinary staff account. The one exception is a session that began with a Microsoft sign-in reporting MFA (the ID token'samrincludesmfa), through either Microsoft button: that session is neither challenged nor sent to enrol, because the second factor was just done at Microsoft. Without that claim the app's own factor applies as usual. An organisation's administrator can require the app's own factor for a particular person even after Microsoft MFA; that setting is theirs, not the person's, and every change is audited. - No phishing-resistant second factor yet. TOTP is the only option; WebAuthn/passkey support (NCSC's current preference alongside TOTP) is not built. Recorded here as an open gap, not implied by omission.
-
TOTP secret storage. Encrypted in the application
before it reaches the database (libsodium secretbox, via
SecretCipher), with the key held in the server’s environment rather than in the database, so a database copy on its own doesn’t reveal anybody’s second factor. Production refuses to start without that key. Backup/reset material is never stored or logged. -
Authorisation. Per-object, checked against the actual
entity by a voter (
OrganisationVoter::MANAGE, coveringBrand, its banners and sections, andUser) in addition to the route-level role check — an organisation's ownROLE_ADMINguessing another tenant's brand or user id in the URL is still refused.TeamFeatureVoterseparately gates team-management features by account type. A platform super admin holds no cross-tenant management access today. -
Tenancy. Every organisation-owned query is scoped by a
Doctrine filter (
OrganisationFilter), parameterised per request from the signed-in user's own organisation — never trusted from a controller or request parameter. A share link is the one deliberate exception: it widens the scope only from the token itself, never from a request parameter, for the one anonymous visitor holding that specific link. -
Brand template rendering is sandboxed. An
administrator's brand template HTML is compiled and rendered through a
dedicated, isolated Twig environment
(
SandboxedBrandTwigFactory) with noappglobal, noconstant()/source()/include()/render(controller(...)), and an explicit allow-list of the constructs real brand templates actually use — never the application's own autowired Twig environment. This exists because it previously wasn't sandboxed: an internal audit found a template-injection path that could have reached server secrets through the admin brand-editing screens, and this is the fix, not a precautionary measure taken in the abstract. -
Share links. A 32-byte random token; only its SHA-256
hash is ever stored. Resolving an unknown, revoked or expired token
returns the same uniform result, so the endpoint can't be used to probe
which links are real. Rate-limited by IP, and every share-link response
sets
noindexandReferrer-Policy: no-referrerso the token in the URL is never handed to a third party in a header or crawled into a search result. -
Response security headers, set on every response: a
Content Security Policy (script execution restricted to a per-request
nonce plus a narrow, documented set of AssetMapper-specific allowances -
no
unsafe-eval),X-Content-Type-Options: nosniff,X-Frame-Options: DENY,Referrer-Policy: strict-origin-when-cross-originby default (tightened tono-referreron share-link pages specifically), andStrict-Transport-Securityon every response that actually arrived over HTTPS. - Encryption in transit. HTTPS only. Cloudflare terminates the connection in front of the server, and HSTS is sent on every secure response.
- Encryption at rest. TOTP secrets are encrypted by the application, as above, and passwords are hashed rather than stored. We haven’t verified whole-disk encryption on the host, so we don’t claim it for anything else.
-
Secrets. Never committed —
.envin this repository ships with every operator-specific value blank and a comment saying to set it in an untracked.env.local.
Audit trail
Administrator actions (role changes, account enable/disable, brand and template changes) write an append-only record: who did it (with the actor's email snapshotted so the entry still says who even after that account is later deleted or anonymised), the organisation it was done in, what it was done to, a free-form before/after context where one applies, the IP address it came from, and when. Entries are kept for two years, purged on that schedule by a scheduled command rather than kept indefinitely by default. An organisation's own administrator can read only that organisation's own entries.
Development practices
- Static analysis at PHPStan level 8, with a baseline recording errors that already existed when analysis was introduced — not a lowered level.
- Coding standards enforced by ECS (PER-CS 2.0) on every change.
- An automated PHPUnit test suite covering tenancy scoping, authorisation, share-link security and the mandatory-MFA gate, run before every change ships.
- All three (
composer qa) gate what's allowed to merge — a red run is not mergeable.
Subprocessors
| Who | What for | What they see |
|---|---|---|
| Vultr (Manchester, UK) | Hosting the application, database, file storage and backups | Everything the service stores |
| Cloudflare (US) | Sitting in front of the site (secure connection and traffic), and Turnstile bot checks on sign-in, registration and password reset | Every request: IP address, browser details and the page asked for, plus the browser signals Turnstile checks |
| Microsoft 365 (email; held in the UK) | Delivering account, invitation and password-reset email. The service hands each message to a mail server on its own hosting, which passes it to Microsoft 365 to deliver | The recipient’s address and the content of the email |
| Brevo (fallback; France, EU) | Kept as a fallback. It delivers email only if the Microsoft 365 route fails and the service is switched to it | Nothing day to day. While it is in use, the recipient’s address and the content of the email |
Page views are counted with Matomo, which we run ourselves at
analytics.mashreeves.com without cookies, so it isn’t a
third party. There’s no advertising or tracking script, so no other
third party sees a visitor or an account holder.
Microsoft isn’t a subprocessor, but it is called. Signing
in with Microsoft uses Microsoft’s sign-in
(login.microsoftonline.com), and an organisation’s group sync
reads Microsoft Graph (graph.microsoft.com) with the permission its
IT team granted. Connecting Microsoft 365 for directory access is separate: a
dedicated app registration with read-only directory access, so that permission
never sits on the sign-in registration other services share. That access is
app-only, granted by one of the organisation’s own Global or Privileged Role
Administrators, limited to one organisation here per Microsoft 365 tenant, and
withdrawable from their Entra admin centre at any time. In every case Microsoft
processes the data for the organisation that holds the Microsoft 365
subscription, not for us.
What a connected directory copies here. Once connected, the directory is synced about once an hour using Microsoft Graph’s change tracking, so after the first sync only changes travel. Only the contact fields a signature needs are requested, and only enabled member accounts with a work email address are kept. For that copy the organisation is the controller and this service its processor. It is visible only to that organisation’s administrators, a leaver’s copy is deleted 30 days after they leave the directory, and disconnecting deletes the whole copy immediately. See the privacy notice.
Availability and resilience
Signature Tool runs on a single Vultr server in Manchester, behind Cloudflare. Vultr takes automatic backups of the whole server, and a restore to a chosen point in time was tested and worked on 10 September 2026. It's one server, not a redundant cluster, so a failure there means downtime until it's restored, and there's no published uptime commitment.
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 Signature Tool has production users depending on one.
Certifications and frameworks
None held by Signature Tool itself. It's a personal project, not operated under an organisation's existing certification. The host is a different matter: Vultr's compliance dashboard lists ISO/IEC 27001 and SOC 2 for its Manchester data centre. That covers the infrastructure, not this service.
Accessibility
Built to WCAG 2.2 AA as the floor. See the published accessibility statement for what's been tested and how, rather than a summary here that could drift from it.
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.