Data storage & residency

Every install is self-hosted. There's no central data store, no telemetry pipeline, no third-party analytics. Customer data lives in your MariaDB / MySQL database on your server. The application runs as a single PHP process — nothing phones home.

PDFs and uploaded files live in a local storage/ directory under your web root. The directory is denied at the web-server level via a triple-layer .htaccess rule (Apache 2.4 + 2.2 + RewriteRule fallback).

Authentication

Password hashing

All user passwords are hashed with bcrypt (cost factor 12). Plain text is never stored or logged.

Sessions

DB-backed sessions, not PHP file sessions. Secure cookie flag set automatically when HTTPS is detected. 30-day idle expiry.

Login rate limiting

Failed login attempts are throttled per email and per IP address. Default: 5 attempts per 15 minutes.

CSRF tokens

Every state-changing form posts a per-session token. Tokens expire on logout and on session timeout.

Security headers

Every HTTP response carries seven security headers. They're applied at the request boundary so coverage is uniform across every page (including share tokens, binary downloads, and the login flow).

HeaderValueWhy
X-Content-Type-OptionsnosniffPrevents MIME-sniffing attacks.
X-Frame-OptionsSAMEORIGINBlocks clickjacking.
Referrer-Policystrict-origin-when-cross-originLimits referrer leakage.
Permissions-Policycamera/mic/geolocation/payment disabledDisables unused browser features.
Cross-Origin-Opener-Policysame-originIsolates browsing context.
Cross-Origin-Resource-Policysame-originBlocks cross-origin asset loads.
Content-Security-Policystrict + 'unsafe-eval' for Alpine.jsXSS protection with Alpine.js compatibility.

Audit log

Every action that mutates state writes to an append-only audit_log table: who did what, when, from what IP address, and what changed. The log is never editable from the application UI — only direct DB access can modify it. Super-admin impersonation events include both the impersonator and the impersonated user in the metadata.

Retention is configurable per install (default: indefinite). Production deployments typically keep 12 months minimum.

Responsible disclosure

Found a security issue? We want to hear about it. Email with the subject prefix Security disclosure.

Please include:

We commit to acknowledging security reports within 48 hours and providing a remediation timeline within 7 days, depending on severity. We're happy to coordinate disclosure timing and credit you in the changelog if you'd like.

Vulnerability history

We publish every fix that addresses a security issue in the changelog with the security tag. There are no silent fixes.