Security & credentials
How we encrypt, isolate, and protect your brand credentials — and what you can do to harden your workspace.
xuly.io stores credentials for up to 221 affiliate programs per customer. Security is designed as the product, not a feature on top.
Credential encryption
Every set of brand credentials is encrypted at rest with pgsodium (libsodium's crypto_secretbox) and stored in the vault.secrets table. The encryption key is managed by Supabase and rotated on their schedule. Integrations reference the secret by UUID — the ciphertext never leaves the database.
Only the sync worker (running as service_role) can decrypt credentials, and only via the get_integration_credentials()RPC. The Next.js web server never sees plaintext credentials after the form submit.
Multi-tenant isolation
Every user-data table has an org_id column and a Row-Level Security policy of the shape:
USING (org_id IN (SELECT org_id FROM memberships WHERE user_id = auth.uid()))A misconfigured query cannot leak cross-tenant data because Postgres enforces RLS at the database level, not application level. We test this with a dedicated RLS test suite on every migration.
Authentication hardening
2FA
Users can enroll in TOTP (Google Authenticator, 1Password, Authy) or WebAuthn passkeys (Touch ID, Yubikey, Windows Hello). On Business and Enterprise plans, 2FA is mandatory — owners can require it workspace-wide.
Sessions
Sessions refresh automatically every hour and expire after 30 days idle. You can see every active session in Settings → Security and revoke any of them individually.
SSO (Enterprise)
SAML 2.0 works with Okta, Azure AD, Google Workspace, JumpCloud — anything that speaks SAML. Contact sales to enable; we provision a SAML connector per workspace.
API keys
API keys are created in Settings → API keys. Every key has:
- Scopes — a subset of the full permission list (e.g.
stats:readonly). - Optional IP allowlist — CIDR blocks that can use the key.
- Expiry date — optional, but recommended for automation keys.
- Last-used timestamp — surfaces unused keys for pruning.
Keys are hashed with Argon2 before storage. The plaintext is shown only once, at creation, and cannot be recovered — revoke and regenerate if lost.
Compliance
- GDPR — full compliance. Data export + delete flows at /legal/privacy.
- SOC 2 Type II — audit in progress. Expected Q3 2026.
- PCI — we don't see or store card data; Stripe handles that in their environment.
Responsible disclosure
Email security@xuly.io with findings. We acknowledge within 24 hours and reward valid reports up to €5,000 for critical issues.