Security
Version 2026-09-28 · Effective 2026-09-28
This page describes how Cart Billing App is built and operated. It covers the measures that are implemented in the codebase and the operational controls around it. It is not a compliance certification, and it does not claim one; where an independent assessment would be needed, we say so rather than assert a result.
We would rather tell you exactly what we do than round it up. If something below reads as thin, that is an accurate reflection of the current state, and we would rather you knew.
1. Reporting a suspected vulnerability
If you believe you have found a security problem, please report it to admin@example.com. Please include what you did, what you expected, the affected endpoint or host, and any proof-of-concept detail. If you have already accessed data that you believe you were not authorised to access, please say so plainly and stop; we would rather work with you on containment than have the disclosure handled as an incident.
- We aim to acknowledge a good-faith report within
securityResponseTimeframe. This is a target for acknowledgement, not a remediation guarantee. - Please do not run automated scanners, denial-of-service tests, or load tests against the Service without written permission.
- Please give us a reasonable window to fix an issue before disclosing it publicly. We are not asking you to keep a vulnerability secret indefinitely, and we will not threaten legal action over good-faith research that stays within these boundaries.
We do not operate a paid bug bounty. We do not have a safe harbour policy of our own, but we will not pursue action against research that follows the guidance above. If you need a formal safe-harbour agreement before testing, ask and we will consider one.
2. How the Service is built
The Service runs as a set of containers on a single host, deployed by a script that builds an image, runs migrations, and switches traffic by moving a release symlink. The components are:
- A reverse proxy that terminates TLS and routes all public traffic. It is the only component exposed to the internet.
- The application, which serves both the authenticated dashboard and portal and the public marketing pages, and which renders on the server for each request.
- A database holding all application data.
- A job runner that executes scheduled work on a timer, separately from request handling.
The database and the job runner sit on an internal network and are not reachable from the internet. Only the proxy publishes ports. The application connects to the database over that internal network, and the application runs under a dedicated database account rather than the administrative one.
Releases are additive and reversible. A deployment keeps the last few releases and the last few database backups on disk, and can roll back to a previous release. Destructive schema changes are handled as separate, explicitly reviewed steps rather than as a side effect of a code push.
Cart Billing App is a multi-tenant application. Every business using it has a separate tenant record, and all business data, users, invoices, and payment records are scoped to that tenant. Access to one tenant’s data does not grant access to another’s; this is enforced in the application on every request that touches tenant data.
3. How your data is protected
Card numbers never reach our servers. Payment details are entered directly into a Stripe-hosted element on a page served by Stripe, and only a token comes back to us. We store that token with a card brand and the last four digits, for display. We do not store full card numbers, security codes, or expiry dates, and we cannot retrieve them.
Passwords are hashed. Passwords are stored as bcrypt hashes, never in a form that could be read back. A database leak does not hand over working passwords.
Second-factor secrets are encrypted. Where staff enable a second factor, the secret is encrypted with AES-256-GCM before it is stored, using a random initialisation vector per secret.
Session tokens are not stored directly. The browser holds a session cookie; the database holds only a hash of it. A database dump therefore does not yield usable session cookies. Sessions are recorded in the database, which means a session can be revoked server-side rather than merely having its cookie cleared.
Transport is encrypted. Public traffic is served over TLS, with HSTS set so browsers refuse to fall back to an unencrypted connection.
Data at rest in the database relies on the encryption of the storage and host it runs on, which is an operational control rather than something the application implements. If you need a specific guarantee here, ask us what the current host configuration is.
Operational access. Access to the production host and its database is restricted to engineers who need it for their work. We do not currently maintain a formal, reviewed, time-limited access process with per-request approval for production data, and we are not going to describe one that does not exist. What we do have is a small team, deployment automation that requires no manual database access, and backups taken automatically.
Access to your data is logged. Administrative actions within the dashboard write an audit record with the actor, the action, a timestamp, and the changes made, so there is a trail to review rather than only a log line to search.
4. Application-level protections
These are the controls in the code itself. We list them because they are verifiable by reading the source, and we would rather you check them than take our word for it.
- Authorisation on every request. Each API route establishes a session and checks the caller’s role before acting. Roles are separated: administrative, billing, and route-operating staff have different access, and customers see only their own account.
- Parameterised database access. All database access goes through the query builder with bound parameters. There is no string-built SQL anywhere in the application, which removes SQL injection as a class of bug rather than trying to filter it out.
- Input validation at the boundary. Request bodies are validated against a declared schema before use, and malformed input is rejected rather than coerced.
- Response headers. A Content-Security-Policy restricts where scripts, styles, and connections may load from; framing is denied; content-type sniffing is disabled; and the referrer policy is set to limit what is leaked to third parties.
- Webhook authenticity. Inbound payment webhooks are verified against a signing secret before the body is parsed, so a forged webhook cannot create a payment.
- Human-verification on the public login. The customer portal login is protected against automated guessing by a proof-of-work challenge that the server independently verifies.
- Throttling on the public payment endpoints. The guest payment lookup and session endpoints are rate limited, so the pay code that grants access to an invoice cannot be brute-forced at speed.
SameSite=Lax, which blocks the common cross-site form submission, and this matters more than usual here because the application is multi-domain and a sibling domain counts as same-site. We consider this a real gap and it is on the list to fix.5. Availability, recovery, and incident response
Backups. The database is backed up as part of a deployment, with a configurable number of recent backups retained on the host. Backups are local to the host, which is a single point of failure; we do not currently replicate them off-host.
Rollback. A release can be rolled back to a previous version, and the previous releases are retained on disk to make that possible.
Scheduled work is separated. Timed jobs run in a separate process from request handling, so a long-running job does not degrade the application for users.
Security incidents. We notify affected customers of a security incident affecting their data without undue delay, and in any case within securityIncidentNoticeHours hours of becoming aware. Customers with a contractual notification deadline shorter than that have dpaBreachNoticeHours hours under the DPA.
On-call and monitoring. We monitor availability and error rates. We do not currently operate a staffed 24/7 security operations centre.
6. Certifications and independent assessment
We are explicit about this section because these are the claims customers most often assume are true.
- SOC 2 Type II: We do not hold a SOC 2 Type II report.
- ISO 27001: We are not certified.
- EU-US Data Privacy Framework: We are not certified, so transfers from the EEA rely on the standard contractual clauses described in the DPA.
- PCI DSS: We are not directly in scope for cardholder data storage. Card details are entered on Stripe-hosted elements and never reach our infrastructure, which keeps the application out of the cardholder data environment. This is a statement about the architecture, not an attestation of compliance.
- Penetration testing: We have not commissioned an independent penetration test of the current release. This is a gap we intend to close.
7. Subprocessors
The providers that process Customer Data on our behalf are listed in section 6 of the Privacy Policy, including what each one processes and where it processes it. Card payments are handled by the payment processor named there; we do not receive card numbers.
We give notice before adding or replacing a subprocessor as described in the DPA, and the current list is always published on the Privacy Policy rather than only in a contract attachment.
8. Security contact
Report suspected vulnerabilities to admin@example.com. For privacy requests rather than vulnerabilities, use the contact in the Privacy Policy.
legalEntityName, entityType, organized in stateOfIncorporation, with a registered address at registeredAddress.