Our Security Model: An Open Technical Review
This article publishes Styrby's security architecture for peer review. Security models should be transparent, not hidden behind a "trust us" statement. If you find a weakness, we want to know. Contact security@styrbyapp.com.
Threat Model
Styrby handles sensitive data: source code, agent conversations, permission decisions, and usage patterns. Our threat model assumes:
- Server compromise is possible. We design so that a server breach does not expose user data.
- Network traffic can be intercepted. All data is encrypted in transit (TLS 1.3) and at the application layer (E2E).
- Client devices can be lost or stolen. Secret keys are stored in system keychains with hardware-backed security where available.
- Insider threat is real. Even Styrby employees cannot read user session data because the server only stores ciphertext.
Encryption Architecture
Data at Rest
| Data Type | Storage | Encryption |
|---|---|---|
| Session messages | Supabase (Postgres) | TweetNaCl box (E2E, server sees ciphertext only) |
| Cost records | Supabase (Postgres) | Plaintext (needed for server-side aggregation) |
| User profiles | Supabase (Postgres) | Plaintext (email, preferences) |
| Public keys | Supabase (Postgres) | Plaintext (public by definition) |
| Secret keys | Device keychain only | System keychain encryption |
| Audit logs | Supabase (Postgres) | Plaintext (needed for compliance queries) |
What the server can see: cost amounts, token counts, agent types, session timestamps, user email, and audit events.
What the server cannot see: source code, agent conversations, file contents, prompt text, or permission request details.
Data in Transit
- All HTTP traffic: TLS 1.3
- WebSocket connections: WSS (TLS 1.3)
- Session message payloads: TweetNaCl box encryption inside TLS
The double encryption (E2E inside TLS) is intentional. TLS protects against network-level interception. E2E protects against server-side access. Both are necessary for the threat model.
Authentication
User Authentication
Supabase Auth handles user authentication with:
- Email/password with bcrypt hashing (12 rounds)
- OAuth (GitHub, Google) for social login
- JWT tokens with 1-hour expiry and automatic refresh
- Rate limiting on auth endpoints: 5 attempts per minute
CLI Authentication
The CLI authenticates using an OAuth-style flow:
- CLI generates a random state parameter and opens a browser URL
- User logs in via the web app
- Web app issues a session token and redirects to a local callback
- CLI receives the token and stores it in the system keychain
- Subsequent API calls use the stored token
The state parameter prevents CSRF attacks during the auth flow. Tokens are stored in the system keychain, not in plaintext config files.
Authorization
All data access is controlled by PostgreSQL Row-Level Security (RLS) policies. Every table has policies that restrict access to the authenticated user's own data:
-- Standard RLS pattern used across all tables
CREATE POLICY "Users access own data"
ON sessions FOR ALL
USING (user_id = (SELECT auth.uid()));
-- The (SELECT auth.uid()) pattern enables query plan caching,
-- which is a significant performance optimization over direct
-- auth.uid() calls.RLS policies are enforced at the database level, not the application level. Even if the application code constructs a bad query, the database rejects unauthorized access.
Infrastructure Security
- Hosting: Vercel (web), Supabase (backend). Both SOC 2 Type II certified.
- Database: Supabase Postgres with encrypted storage and automated backups.
- Secrets management: All environment variables stored in Vercel/Supabase dashboards. No secrets in code or config files.
- Dependencies: Automated vulnerability scanning with
npm auditin CI. Critical vulnerabilities block deployment.
What We Would Do Differently
Honest assessment of decisions we would reconsider:
- Forward secrecy. Our current encryption uses long-lived key pairs. A Double Ratchet protocol would provide forward secrecy, meaning a compromised key would not decrypt past messages. We may add this for the enterprise tier.
- Key rotation. We do not have an automated key rotation mechanism. Adding one requires either re-encrypting historical data or accepting split access. This is on the roadmap.
- Cost data encryption. Cost records are stored in plaintext for server-side aggregation. We could encrypt them and decrypt client-side, but this would prevent server-side budget checking. The tradeoff favors functionality over privacy for cost data specifically.
Reporting Vulnerabilities
If you find a security issue, email security@styrbyapp.com. We commit to acknowledging reports within 48 hours and providing a timeline for fixes within one week. We do not have a formal bug bounty program yet, but we will credit researchers in our changelog.
The Styrby CLI source code is available on GitHub. We encourage security researchers to review the encryption implementation, the authentication flow, and the permission handling logic.
Ready to manage your AI agents from one place?
Styrby gives you cost tracking, remote permissions, and session replay across five agents.