Security
BriefGate asks your clients for logos, copy — and passwords. That last one changes what this product is: a service that collects credentials is infrastructure, and infrastructure has to say plainly what it does and does not protect you from.
Everything below describes the system as it is built today. Where a protection does not exist, this page says so rather than leaving it out.
What BriefGate is not
It is not zero-knowledge. A secret value is encrypted with a libsodium
sealed box the moment it arrives, and the private half of that key pair lives in
the server environment, not in the database. A stolen database dump therefore
yields ciphertext alone. But the server that receives the value can read it —
that is how it hands the value to your agent — so an attacker who owns the
running process could read a secret in flight. Any product claiming otherwise
while holding the key is claiming something it cannot deliver.
It holds no certification. There is no ISO 27001, no SOC 2, and no external penetration test. BriefGate is operated by one person. This is stated because a security assessment should rest on what exists.
Secrets
A secret item is the only field type that gets its own handling end to end.
- The value is encrypted on receipt with a libsodium sealed box
(
crypto_box_seal) and is never written to disk in plaintext. - The private key is held in the server environment and is never stored in the database, so a full database dump cannot be decrypted.
- Reading it requires an API key carrying the
secrets:readoradminscope. A key without that scope is told the value exists and nothing more. - It is released once. The retrieval is atomic: the first read marks it revealed, and a later read returns the time of that reveal instead of the value. A five-minute window after the first reveal lets the same key repeat the same read, so a request lost in transit does not cost you the value.
- Every reveal is written to the audit log with the key that did it.
- The dashboard cannot show a secret. There is no screen, anywhere, that
renders one — the only way out is
get_intake_resultswith the right scope.
What this buys you: a client can send a WordPress password to your agent without it sitting in an inbox, a chat log, or a support ticket.
Credentials and sessions
| Account passwords | argon2id |
| API keys, portal tokens | 256 bits of randomness, stored as a SHA-256 digest; plaintext shown once at creation |
| Two-factor | TOTP, optional, per user |
| Client portal access | single-use magic link, exchanged for a session cookie scoped to that one intake |
| Portal session lifetime | 14 days |
| Login and sign-up | 10 attempts per hour per IP address |
API keys are hashed with SHA-256 rather than argon2id on purpose. The key is 256 random bits, so there is no dictionary to slow an attacker down with, and a password hash on every agent request would cost tens of milliseconds for no security gain. Passwords, which people choose, get argon2id.
A password reset invalidates every session and does not issue a new one, so TOTP still stands between an attacker with a reset link and the account.
Files
- Served only through signed URLs valid for 24 hours. The URLs are not guessable and not listable.
- Scanned for malware before they are made available. A file whose scan errors or times out is withheld, not served unscanned.
- Every file carries a SHA-256 checksum, computed on receipt and returned with the results, so you can verify that what you downloaded is what the client uploaded.
- Stored in Cloudflare R2 with the EU jurisdiction restriction.
Webhooks
The endpoint you register receives every event on the account, so it is treated as an account-wide decision: only the account owner can add, remove or test one, and creating one issues a signing secret shown once.
- Every delivery is signed:
X-BriefGate-Signature: t=<unix>,v1=<hmac-sha256>over<timestamp>.<body>. Verify it before trusting a payload. - The target address is re-resolved and re-checked immediately before each
request leaves the box, not only at registration. Checking only at
registration is a time-of-check/time-of-use gap: a hostname that resolved to a
public address then can resolve to
169.254.169.254now. - Private, loopback, link-local and unique-local ranges are refused, in both their IPv4 and IPv6-mapped spellings.
- Redirects are not followed, so a redirect cannot walk the request into a private address.
- Each attempt times out after 10 seconds; the response body is drained to a 4 KB cap so a hostile endpoint cannot exhaust memory.
- Seven attempts with exponential backoff, then the delivery is marked failed and can be sent again by hand from the dashboard.
410 Gonefrom your endpoint deactivates it — the documented way to unsubscribe from the receiving end.
The payload never contains a portal link. A portal link is a bearer credential into your client's intake, and a chat channel usually has more readers than the intake does.
Where the data is
| Application and database | netcup GmbH, Nuremberg, Germany |
| Files | Cloudflare R2, EU jurisdiction restriction |
| Transactional email | Resend (US) — EU-U.S. Data Privacy Framework and SCCs 2021/914 |
| SMS, only if you enable it | Twilio (US) — same basis |
| Payments | Stripe (US) — billing data only, never client data |
The full list, with the transfer basis for each, is in the Data Processing Agreement.
Retention and deletion
- Default: contents removed 90 days after completion. Configurable from 1 to 3650 days, per account or per intake.
retention.mode: "on_delivery"removes contents roughly 24 hours after your agent collects the results — the moment the second copy on our server stops being a service and starts being a liability. Use it for anything with credentials in it.DELETE /v1/intakes/:idremoves the intake, every submitted value, every file in R2 and the whole reminder history, immediately and irreversibly.- Deleting your account marks it immediately and completely; the purge runs after 30 days.
- Backups are encrypted with
ageto a public key before they leave the host, so the machine that writes a backup cannot read it back, and the private half is not kept on that machine. They are held for 14 days, which is the window in which deleted data can still exist in a backup.
Auditing
Sensitive operations are written to an append-only audit log recording the
actor, the action, the IP address and the timestamp: secret reveals, key
creation and revocation, seat changes, logins. The application exposes no path
that deletes from it. You can read your own account's log in the dashboard or
through GET /v1/audit.
Abuse and rate limits
- API requests: 60, 600 or 3000 per hour depending on plan.
- Invitations and manual reminders are capped per intake and per account, so neither the API nor the dashboard can be used to send mail at someone from our domain. When a cap is hit the intake is created as a draft and the response says so, rather than silently not sending.
- Reminders respect quiet hours and stop on a hard bounce or spam complaint — a bounced address is never chased again.
Tracking
There is none. No analytics, no tag manager, no session recording, no error reporting service, no advertising pixel — on the marketing site, in the dashboard, or in the client portal. The only cookies are the ones that make sign-in and the portal session work; they are listed in the Privacy Policy.
Reporting a vulnerability
Email [email protected] with enough detail to reproduce it. You will get a human reply. Please do not run automated scanners against the production service or test against another customer's account or portal — report it and it will be checked properly.