Skip to content

Security Architecture

PrincipleImplementation
Defense in DepthTLS, JWT, RLS, Argon2id, CSP headers
Least PrivilegeRole-based access control (RBAC)
Secure by DefaultSQLx compile-time query validation
Fail SecurelyRust’s Result type for error handling
Zero TrustJWT validation on every API call
  • Minimum 12 characters with uppercase, lowercase, digit, and special character
  • Hashing: Argon2id with OWASP parameters: m=65536 (64 MiB), t=3, p=4

Stateless authentication via JSON Web Tokens (HS256):

pub struct Claims {
pub sub: String, // User ID
pub exp: usize, // Expiration time
pub iat: usize, // Issued at time
pub role: UserRole, // User role
}
SettingValue
Access token expiration1 hour (default, configurable)
Refresh token expiration7 days
RevocationRedis-backed (JTI denylist)
SecretJWT_SECRET, minimum 32 characters

Token storage by platform:

PlatformMethod
Web (Leptos)HttpOnly cookies
Desktop (Tauri)Secure storage API
Mobile (Tauri)Keychain/Keystore
pub enum UserRole {
Admin,
Moderator,
Homeowner,
Tenant,
Developer,
}
ResourceAdminModeratorHomeownerDeveloperTenant
/admin/*
/moderator/*
/developer/*
/user/profile
/public/*

PostgreSQL RLS policies enforce access at the database level on 5 tables — users, properties, bids, payments, and notifications — providing a second layer of defense beyond application-level RBAC.

  • Protocol: TLS 1.3
  • Cipher Suites: AEAD only
  • Certificates: Let’s Encrypt (auto-renewal via cert-manager)
  • HSTS: Enabled with long max-age

Redis-based, tiered per-endpoint limits:

TierRequestsPeriodApplied To
Auth1560s/auth/*
Public10060s/search/*
User100060sAll protected routes

Limits are enforced per IP and per user. If Redis is unavailable, rate limiting degrades gracefully and all requests are allowed.

Explicit origin whitelist via the CORS_ALLOWED_ORIGINS environment variable (comma-separated). Never set to * in production.

CSRF protection via middleware with origin validation on state-changing requests.

All API responses include:

HeaderValue
Strict-Transport-Securitymax-age=63072000; includeSubDomains; preload
X-Frame-OptionsDENY
X-Content-Type-Optionsnosniff
Content-Security-Policydefault-src 'none'; connect-src 'self'; frame-ancestors 'none'
Referrer-Policystrict-origin-when-cross-origin
Permissions-Policycamera=(), microphone=(), geolocation=()
Cross-Origin-Embedder-Policyrequire-corp
Cross-Origin-Opener-Policysame-origin
  • Serde deserialization with validator derive macros
  • Ammonia HTML sanitization on user-generated content
  • Parameterized SQL queries via SQLx (compile-time validated)

SQLx validates all queries at compile time:

sqlx::query_as!(
User,
"SELECT id, email, role FROM users WHERE id = $1",
user_id
)
.fetch_one(pool)
.await

Leptos automatically escapes HTML output. No raw HTML rendering without explicit inner_html.

  • JWT authentication required in handshake
  • Message type validation before processing
  • Rate limiting on connections
  • PostgreSQL Transparent Data Encryption (TDE) on production
  • pgcrypto extension for sensitive field encryption
  • Tauri secure storage for mobile/desktop secrets

Sensitive fields are excluded from API responses via #[serde(skip)]:

pub struct UserPublic {
pub id: i32,
pub email: String,
#[serde(skip)]
pub password_hash: String,
}

Aggregate data (bloc compositions, owner identities) is exposed only after escrow deposit, protecting negotiation privacy.

  • Cardholder data: processed entirely by Stripe (PCI DSS Level 1); never stored locally
  • Bank payments: processed by TrueLayer, an FCA-authorised Payment Institution
  • Webhook verification: HMAC-SHA256 signatures for both Stripe and TrueLayer deliveries
  • Fee calculation: buyer pays on top with a transparent breakdown
ControlTool
Dependency auditingcargo-deny
Secret scanninggitleaks
SBOM generationSyft (SPDX-JSON)
Image signingCosign
Container scanningTrivy
SettingValue
Session Timeout24 hours
Idle Timeout1 hour
Secure FlagEnabled
SameSiteStrict