1. Roles
The Founder is the key custodian: generates, stores, rotates, revokes and destroys every platform key, and approves any change to cryptographic settings. From the first hire, a second person may be named custodian for specific keys, recorded in the security register. Customers manage their own API keys (policy 11).
2. Approved algorithms
Only standard, well-reviewed algorithms from maintained libraries are used. No home-made cryptography.
| Use | Algorithm |
|---|---|
| Data in transit | TLS 1.2 or 1.3 (Caddy automatic HTTPS, publicly trusted certificates via ACME) |
| Application encryption | AES-256-CBC with HMAC-SHA256 (Laravel encrypter, keyed by APP_KEY) |
| Passwords | bcrypt |
| API key storage | SHA-256 of a 40-character random token, compared in constant time |
| Audit log integrity | SHA-256 hash chain |
| Webhook verification | HMAC-SHA256 (WhatsApp, when enabled) |
| GitHub App authentication | RS256-signed JWT, 10-minute lifetime |
| Server access and deploy | SSH with ed25519 keys |
SHA-1 appears only inside TOTP (RFC 6238 HMAC-SHA1), as the standard requires for compatibility with authenticator apps.
3. Data in transit (in place)
- All web and API traffic is HTTPS only, with HSTS (
max-ageone year,includeSubDomains). - Outbound calls to suppliers (e-mail, GitHub, AI provider) use HTTPS.
- Administration uses SSH; code is pulled over Git with SSH.
- Inside the server, PHP talks to PostgreSQL (SSL enabled, client mode
prefer) and Redis on the same machine; Redis traffic is not encrypted. Their ports are closed in the firewall.
4. Data at rest
Encrypted by the application today (verified in code):
- users' TOTP secrets and MFA recovery codes (
encryptedcasts); - session payloads (session encryption enabled);
- passwords (bcrypt hash) and API keys (SHA-256 hash) are never stored in readable form;
- in the lead, vendor lookup, verification request and watch-list tables, visitors' IP addresses are stored only as a salted SHA-256 hash (the audit log, by design, keeps the IP address of the events it records - policy 04).
Not encrypted today (disclosed):
- The VPS disk and the PostgreSQL database files are not encrypted at the disk or volume level. Customer data other than the fields above (organization settings, agent inventory, policies, decisions with masked parameters, audit log) is stored in clear in the database.
- Database backups are plain
pg_dumpfiles on the same disk (policy 06).
Commitments:
- Encrypt backups before they leave the server, as part of off-site backup (owner: Founder, target 2026-10-31).
- Assess encryption at rest: volume encryption if the host allows it, or moving to a host that offers encrypted disks, and field-level encryption for any data classified Restricted (policy 08). Decision recorded in the register (owner: Founder, target 2027-03-31).
Customer-managed encryption keys (BYOK) are not offered.
5. Key inventory
| Key or secret | Where it lives | Rotation |
|---|---|---|
| APP_KEY (256-bit) | server .env, restricted permissions |
On suspected compromise; otherwise reviewed yearly |
| Database password | server .env |
On suspected compromise or staff change |
| Database owner password (migrations only; in rollout) | /etc/semantiqwall/db-owner.env, owned by root, mode 0600 (folder 0700), outside the application folder, never in the application .env; read by the deploy script only while migrating |
On suspected compromise or staff change |
Copy of .env taken before the database-roles rollout (holds the same secrets as .env at that time, including the database owner password) |
/root/semantiqwall.env.antes-papeis, owned by root, mode 0600; kept only as the rollback copy |
Deleted once the rollout is confirmed (owner: Founder, target 2026-10-31); rotated with the secrets it holds |
| TLS certificates | Caddy storage on the server | Automatic renewal before expiry (about every 60-90 days) |
| Deploy key (read-only, ed25519) | server, root-only | Yearly, or on compromise |
| Founder SSH keys | Founder's devices | Yearly, on device change or loss |
| GitHub App private key | file outside the repository, path in config | Yearly, or on compromise |
| Supplier API keys (Resend, AI provider, WhatsApp) | server .env |
Yearly, or on compromise or staff change |
| Customer API keys | SHA-256 hash in the database | Customer decides; revocable at any time |
Keys are generated with cryptographically secure generators (random_bytes, openssl rand, ssh-keygen, php artisan key:generate, ACME). Each key has one purpose. The inventory is kept current in the security register.
6. Rotation, revocation and destruction
- Routine rotation: once a year at the policy review for the keys marked yearly; recorded in the register.
- Revocation on compromise (any suspicion is enough): open an incident (policy 05); generate the new key; install it; revoke the old one at the supplier (GitHub deploy key, GitHub App key, supplier dashboards) or remove it from
authorized_keys; confirm the service works; record in the register. - APP_KEY rotation: put the current key in
APP_PREVIOUS_KEYSand set a newAPP_KEY, so existing encrypted MFA secrets remain readable; re-save encrypted fields under the new key; then remove the previous key. Side effects: all sessions end and outstanding signed links (watch-list confirmation and unsubscribe) stop working; IP hashes created after the change no longer match earlier ones. - Destruction: retired keys are deleted from the server and devices, removed from suppliers, and never reused. Keys are never kept in archives or backups outside the server
.env, except the root-only (0600) files in the inventory above (/etc/semantiqwall/db-owner.envand the rollback copy/root/semantiqwall.env.antes-papeis). The SQL file the rollout generates (/root/sw-papeis.sql, root-only 0600) holds no password: the application password is passed topsqlas a variable. - Compromised keys are never used to encrypt new data; they are kept only as long as needed to decrypt and re-encrypt existing data, then destroyed.
7. Monitoring
- Creation and revocation of customer API keys, MFA enrolment and MFA reset are written to the audit log.
- Changes to platform keys are recorded manually in the security register. Automatic logging of platform key events is not in place.