Security & privacy
Covenant is built local-first and privacy-first. The free tier makes no network calls at all, and every networked feature is designed so sensitive data physically cannot leak.
Local-first by default
- All your data lives in your browser's
localStorageundercovenant.v1. Nothing is uploaded unless you sign in to the cloud tier. - The app boots with zero non-local network calls. Authentication scripts load only when you explicitly click Sign in.
- Clearing browser data or switching browsers/devices starts fresh — use the JSON export to back up.
The SSRF guard (posture scan)
A vendor's domain is untrusted input. Before any live probe, Covenant refuses IP literals, private/loopback/link-local/CGNAT ranges, embedded credentials, explicit ports, internal/reserved TLDs, and anything that isn't a valid public DNS hostname. This prevents the scan from being turned into a tool to reach internal infrastructure. See External posture scan → SSRF guard.
No secrets in payloads; masked targets
- Notification payloads carry only a template id and variables (names, link, dates). A guard refuses to send if it detects a secret or personal-identifier pattern.
- Recipient addresses are masked in confirmations and stored masked in the local outbox — never verbatim.
The PHI-safe boundaries
- Cloud sync pushes only structured vendor-risk facts (tier, score, grade, BAA state, finding counts). There is no getter for free-text BAA notes or document contents, so they cannot reach the cloud store.
- Evidence publish emits a metadata/ref object only — no free text, no PHI, no document bytes.
- Document bytes upload browser → R2 via presigned URLs; they never pass through a Dosanjh Labs server.
Client-direct AI
The optional AI assistant calls your chosen provider directly from your browser with your own key; the key never touches any Dosanjh Labs server, prompts are data-minimized, and a scrubber hard-blocks any send that looks like it contains an identifier or secret. See Settings & AI.
Tenant isolation
In the cloud tier the client never sends a tenant id; the server derives it from your verified session, so you can only read and write your own tenant's data.
localStorage when done, or use a private browser profile.