Property PrismDev Hub

Security Architecture

Comprehensive security model including authentication, authorization, org isolation, data protection, and operational security.

Updated Apr 3, 2026

Security Layers

LayerSystemControls
User AuthenticationClerkSSO, MFA, session management, JWT issuance
User AuthorizationBackend middlewareScope-based permission enforcement
Org IsolationBackend + Database RLSMulti-tenant data separation
Service AuthPrism API KeysScoped, expiring machine tokens
TransportCloudflareTLS termination, HSTS, WAF
InfrastructureGitHub, Render, Supabase, InfisicalPlatform-level access controls

Authentication

Clerk Integration

Prism uses Clerk for all user authentication:

  • SSO — single sign-on, no direct username/password login
  • MFA — required for all accounts with production system access
  • JWT tokens — short-lived bearer tokens with org context
  • Session management — handled entirely by Clerk

JWT Claims

The Clerk JWT contains:

{
  "sub": "user_abc123",
  "org_id": "org_xyz789",
  "org_permissions": [
    "org:buildings:read",
    "org:buildings:write",
    "org:comps:read",
    ...
  ]
}

Backend JWT Validation

  1. Frontend sends Authorization: Bearer <token> on all API requests
  2. Backend validates JWT signature against Clerk JWKS endpoint (CLERK_JWKS_URL)
  3. Backend verifies issuer (CLERK_ISSUER) and optionally audience (CLERK_AUDIENCE)
  4. Backend extracts sub, org_id, and org_permissions from claims
  5. Scope normalization: org:buildings:readbuildings:read

Strict Scope Mode

STRICT_CLERK_SCOPES=true must be set in production:

  • When enabled: missing or empty org_permissions = zero scopes (deny-by-default)
  • When disabled: missing permissions may fall back to a default grant (development only)

Authorization (RBAC)

Role Definitions

RoleDescriptionScopes
ViewerRead-only stakeholdersAll *:read scopes
MemberStandard operational usersAll *:read + *:write + export
AdminOrg owners/operatorsAll member scopes + api_keys:manage, audit:read, export:admin

Complete Scope List

Read Scopes (Viewer+):

  • buildings:read, comps:read, tims:read (mapped from tim:read)
  • tenants:read, owners:read, contacts:read, brokerage_firms:read
  • reports:read, building_parks:read, key_points:read

Write Scopes (Member+):

  • All *:write variants of the above
  • export — access to data export

Admin Scopes:

  • api_keys:manage — create, list, revoke API keys
  • audit:read — access audit log
  • export:admin — BI export management

Enforcement Points

  1. Backend middleware (RequireScope) — checks scope on every protected route
  2. Admin middleware — additional gate for admin-only routes (/admin/*)
  3. Frontend UI (AuthGate, AdminRouteGate) — convenience-only, not authoritative
  4. Database RLS — row-level enforcement on all org-scoped tables

Canonical scope definition: go-backend/internal/domain/auth.go
Scope normalization: go-backend/internal/middleware/auth.go


Organization Isolation

Multi-Tenant Model

Prism is a multi-tenant application where each organization's data is fully isolated:

  1. JWT-based org context — org ID extracted from Clerk JWT claims
  2. Backend enforcement — org ID passed to all repository queries for org-scoped resources
  3. Database RLS — Row-Level Security policies enforce isolation at the database layer

Data Scoping

ScopeTablesIsolation Method
Publicbuildings, geography, lookups, key_points, labor_blocksNo org filter, authenticated read
Org-Scopedcomps, TIMs, owners, tenants, contacts, reports, audit_log, etc.organization_id column + RLS

RLS Policy Functions

FunctionPurpose
can_org_read(org_id)User belongs to the org
can_org_write(org_id)User is an active member of the org
is_org_admin(org_id)User has admin role in the org

Cross-Org Protections

  • Triggers enforce org consistency on join tables (e.g., tim_comp_org_consistency() ensures TIM and comp belong to same org)
  • Building overrides are scoped per org
  • Export schema provides org-filtered views for BI tools
  • API keys are scoped to specific organizations

API Key Authentication (Service-to-Service)

For machine-to-machine access (scripts, integrations, BI tools):

Key Properties

  • Organization-scoped — each key belongs to one org
  • Scoped permissions — array of allowed scopes
  • Expiring — optional expires_at date
  • Revocablerevoked_at for immediate invalidation
  • Hashed storage — raw key shown once at creation, only hash stored
  • Admin-only management — requires api_keys:manage scope

Safe View

The api_keys_safe database view excludes key_hash and adds a computed is_active field. Direct table SELECT is revoked for the authenticated role.


Data Protection

Transport Security

  • All traffic encrypted via TLS (terminated at Cloudflare edge)
  • HSTS enforced
  • Backend port :8080 not publicly accessible
  • CORS allowlists explicit per environment

Data at Rest

  • Database hosted on managed Supabase Postgres (encrypted at rest)
  • Redis hosted on managed Upstash (encrypted at rest)
  • No secrets in source code or git history

Secrets Management

  • All secrets in Infisical (centralized, auto-synced to platforms)
  • VITE_* frontend variables are considered public
  • No long-lived backend API keys in frontend runtime
  • Rotation policy: 90 days for DB/Redis credentials, on-change for auth keys

Data Deletion

  • See docs/security/data-deletion-policy.md for data retention and deletion procedures
  • Audit log is append-only (no deletion)

Security Headers

The backend sets these security headers via SecurityHeaders middleware:

HeaderValuePurpose
Strict-Transport-Securitymax-age=31536000; includeSubDomainsForce HTTPS
X-Content-Type-OptionsnosniffPrevent MIME sniffing
X-Frame-OptionsDENYPrevent clickjacking
X-XSS-Protection0Disable legacy XSS filter (rely on CSP)
Referrer-Policystrict-origin-when-cross-originLimit referrer info
Permissions-PolicyRestrictiveDisable unused browser features

Rate Limiting

  • Algorithm: Token bucket per client IP
  • Backend: Redis-backed rate limiter (middleware/rate_limit_redis.go)
  • Configuration: RATE_LIMIT_PER_SECOND, RATE_LIMIT_BURST env vars
  • Cloudflare real IP: When TRUST_CLOUDFLARE_HEADERS=true, rate limits use the real client IP behind Cloudflare

Request Tracing

  • InjectRequestID middleware generates a unique request ID per request
  • Request ID is propagated to:
    • Error responses (request_id field)
    • Structured log entries
    • Error log table entries
    • Sentry error reports
  • Frontend surfaces request ID in user-visible error details

Error Capture & Audit

Error Log

The error_capture middleware writes 4xx/5xx responses to the error_log table:

  • Channel, severity, message, HTTP path/method/status
  • Request ID, user ID, stack trace
  • RLS: insert for org members, select for admins only, no update/delete

Audit Log

The audit middleware writes data mutations to the audit_log table:

  • Actor (user ID, org ID), action, target table/record
  • Changed fields, old/new payloads
  • Network metadata (IP, user agent)
  • Append-only: insert allowed, update/delete denied via RLS

Sensitive Field Handling

API key audit logs use an allowlist — only name, scopes, expires_in_days are logged from request bodies. Sensitive fields like key hashes are excluded.


Infrastructure Access Control

SystemAdminStandard Access
GitHubEngineering lead (Owner/Admin)Active engineers (Write)
InfisicalBackend lead / Ops leadEngineers (read-only prod)
Supabase/RDSBackend lead (break-glass)API process only (prism_app role)
RedisBackend lead
CloudflareOps lead
SentryBackend leadActive engineers (Member)
RenderOps lead
Clerk DashboardBackend lead

Database Role Model

RolePrivilegesUsed By
postgres (superuser)Full accessBreak-glass only
prism_appUSAGE on public, CRUD via RLSAPI process
authenticatedRead public, org-scoped CRUD via RLSSupabase auth context
service_roleExecute refresh/snapshot functionsScheduled jobs, export refresh

The prism_app role has no SUPERUSER and no BYPASSRLS — all access goes through RLS policies.


Incident Response

See docs/security/incident-response.md for full procedures.

Severity Levels

SeverityResponse TargetExample
P1 — Critical< 15 minProduction down, confirmed data breach
P2 — High< 1 hourElevated 5xx, auth failures, critical CVE
P3 — Medium< 4 hoursSingle endpoint issue, staging problem
P4 — LowNext business dayNon-critical dep alert, doc drift

Detection Sources

  • Sentry alerts (error spikes, performance degradation)
  • Render service metrics (CPU, memory, restarts)
  • GitHub secret scanning / Dependabot alerts
  • External reports
  • Manual observation

Vulnerability Management

See docs/security/vulnerability-management.md for full procedures.

  • Dependabot enabled for Go modules and npm packages
  • CodeQL static analysis in CI
  • Critical/high vulnerabilities addressed within SLA
  • Regular dependency updates

  • Access control policy: docs/security/access-control-policy.md
  • Secrets management: docs/security/secrets-management.md
  • Incident response: docs/security/incident-response.md
  • Vulnerability management: docs/security/vulnerability-management.md
  • Backup and DR: docs/security/backup-restore-and-dr.md
  • Data deletion: docs/security/data-deletion-policy.md
  • Backend security audit: docs/security/security-audit-2026-04-01.md