Trust Center
Security & Privacy Architecture
StrataView stores data used in regulatory proceedings and legal exhibits. This page describes how that data is protected, who can access it, and how incidents are handled. Last reviewed: June 2026.
Overview
Architecture principles
StrataView is a Netlify-hosted static site backed by Supabase for authentication, database, and storage. All sensitive operations run in Netlify Functions (Node.js) or Supabase database functions. The browser never receives a service-role key. Every assessment record and audit event is stored in Supabase Postgres with row-level security (RLS) enforced at the database layer.
Assessment records produced by StrataView are used as exhibits in environmental permitting proceedings and legislative filings. The security model reflects this: our primary concern is data integrity (can a record be silently altered?) and data isolation (can one tenant see another's data?), not just confidentiality.
Identity & Authentication
Who can access what
Authentication is handled by Supabase Auth (GoTrue), a purpose-built identity service based on PostgreSQL. StrataView does not implement a custom authentication stack.
Authentication flow
- User enters email + password at
/login/. - Supabase Auth validates credentials and issues a short-lived JWT (1 hour) plus a long-lived refresh token stored in
localStoragebehind the site origin. - If the user has enrolled TOTP MFA, the session is held in the "aal1" assurance level until the TOTP challenge is satisfied, which upgrades it to "aal2". Protected routes require aal2.
- JWTs are validated at the database layer via Supabase's
auth.jwt()function. The application server never stores session state.
TOTP MFA
Time-based one-time passwords (TOTP / RFC 6238) are available for all accounts. New users are prompted to enroll during first login. Enrollment state is tracked in Supabase's auth.mfa_factors table and surfaced in the app shell. TOTP codes are verified against the Supabase MFA challenge API. The TOTP seed never transits the application layer.
Optional SAML SSO (Enterprise)
Enterprise-tier tenants may configure SAML 2.0 single sign-on via Supabase's identity provider integration. SAML assertions are validated by Supabase; the SP-initiated login flow redirects to /app/ on success. SAML configuration is performed in the Supabase Auth dashboard under "SSO Providers" and requires a dedicated subdomain or relay state parameter.
Password policy
Supabase Auth enforces a minimum password length of 12 characters. Passwords are stored as bcrypt hashes (cost factor 10) within the Supabase-managed auth.users table, which is inaccessible to application-layer RLS policies.
Data Isolation
Row-level security per tenant
Every table in the app schema carries a tenant_id column. Row-level security policies enforce that authenticated queries can only return or mutate rows where tenant_id matches the tenant extracted from the caller's JWT, using the app.jwt_tenant_id() helper function which reads the custom claim injected at login.
tenants, app_users, audit_events, rate_limits, tenant_counties, briefs, brief_grants, contact_messages. Disabling RLS on any table requires explicit migration and review.
The public_analytics schema stores visitor and session data. It is isolated from the app schema. Analytics tables have no tenant_id and are not accessible to tenant-scoped JWT tokens. Analytics reads require the service-role key, which is restricted to Netlify Functions.
Tenant provisioning
Tenants are created by the StrataView administrator using the service-role key. There is no self-service tenant creation path. New users are added to a tenant by the tenant owner; they cannot add themselves. User shell records are pre-created in app.app_users with status 'invited'; the on-signup database trigger links the Supabase Auth user ID on first sign-in.
Audit Trail
Hash-chained audit events
Every write to any application table appends a row to app.audit_events. Each event carries:
id: UUID (immutable primary key)tenant_id: owning tenantactor_id: theapp_users.idwho performed the actionaction: verb (INSERT, UPDATE, DELETE, LOGIN, EXPORT, etc.)table_name,row_id: subject of the actionpayload: JSONB snapshot of changed fields (new values only; passwords excluded)prev_hash: SHA-256 hash of the preceding event row in insertion orderevent_hash: SHA-256 of(id || actor_id || action || payload || prev_hash)created_at: server-side timestamp (not client-supplied)
The app.verify_audit_chain() Postgres function re-computes every hash in sequence and returns the count of mismatches, the total rows checked, and a valid boolean. The API endpoint GET /api/audit/verify exposes this to authorized callers.
Audit rows are append-only. There are no UPDATE or DELETE policies on audit_events, not even for the service-role key at the application layer. Any modification attempt would require direct database access, which is logged by Supabase's audit logging at the infrastructure level.
Encryption
In transit and at rest
Transport (TLS 1.3)
All traffic between clients and strataview.earth is encrypted with TLS 1.3 via Netlify's edge. HTTP requests are permanently redirected to HTTPS (301). The Strict-Transport-Security header is set with a two-year max-age and includeSubDomains. TLS termination for Supabase API calls uses the same TLS 1.3 standard on AWS infrastructure.
At-rest encryption (AES-256)
Supabase Postgres storage is encrypted at rest with AES-256 on AWS EBS volumes. Supabase manages key rotation on a schedule controlled by AWS KMS. StrataView does not operate its own encryption keys for database storage. Key management is delegated to Supabase and AWS. This is documented for SOC 2 purposes as a sub-processor control.
api-brief-grant.js using a separate HMAC token. The token is short-lived (15 minutes) and single-use, marked in the database.
SOC 2 Common Criteria
Key control mappings
The following table maps StrataView controls to SOC 2 Trust Services Criteria (2017 version). This is a self-assessment and has not yet been independently audited.
| CC Criterion | Description | StrataView Implementation | Status |
|---|---|---|---|
| CC6.1 | Logical access security | Supabase Auth JWT + RLS per tenant; TOTP MFA available | Implemented |
| CC6.2 | New user provisioning | Pre-created invited shells; owner-only seat assignment; on-signup trigger links auth ID | Implemented |
| CC6.3 | Access removal | Offboarding runbook: set status='suspended', invalidate Supabase session, revoke grants |
Implemented |
| CC6.7 | Data transmission controls | TLS 1.3 enforced; HSTS with 2-year max-age; all APIs HTTPS-only | Implemented |
| CC7.2 | Monitoring for anomalies | Rate-limit table + rate_limit_check(); Netlify function logs; Supabase log drain |
Partial |
| CC7.4 | Incident response | Documented runbook in SECURITY-RUNBOOKS.md; security@ contact; 72hr breach notification target | Implemented |
| CC9.2 | Risk assessment | Architecture review (StrataView-Architecture-Review.md); annual re-review committed | Partial |
| A1.2 | Availability & capacity | Netlify Edge CDN; Supabase managed Postgres; no single-host dependency | Implemented |
| PI1.1 | Processing integrity | Hash-chained audit log; verify_audit_chain(); append-only audit table |
Implemented |
| P3.1 | Privacy notice | Privacy policy at /legal/privacy/; analytics self-hosted; no third-party trackers | Implemented |
Incident Response
How we respond to security events
Security incidents are reported to security@strataview.earth. Our responsible disclosure policy is at /.well-known/security.txt. We acknowledge reports within 48 hours and commit to status updates every 7 days until resolution.
Breach notification
In the event of a confirmed data breach affecting personal data, we will notify affected users and, where required, the applicable supervisory authority (Michigan Attorney General consumer protection division for Michigan residents) within 72 hours of confirmation. Notification will include: nature of the incident, categories of data affected, likely consequences, and measures taken or proposed.
Runbooks
Detailed operational runbooks are maintained in SECURITY-RUNBOOKS.md in the project repository. Topics include: incident response, suspected account takeover, credential rotation, data subject requests, audit chain verification, disaster recovery test, and user offboarding.
Compliance Posture
SOC 2 readiness statement
StrataView is designed in alignment with SOC 2 Type II Trust Services Criteria for Security, Availability, and Processing Integrity. As of June 2026, we are in the pre-audit phase: controls are implemented and documented, but an independent audit has not yet been conducted.
We are evaluating Vanta and Drata for continuous compliance monitoring. Quarterly audit chain verification runs are automated via the soc2-evidence.yml GitHub Actions workflow, which calls /api/audit/verify and stores the JSON result as a signed GitHub Actions artifact.
Third Parties
Sub-processors
StrataView uses the following sub-processors that may handle personal data. We do not use third-party analytics, advertising, or tracking services.
| Sub-Processor | Role | Data Processed | Location |
|---|---|---|---|
| Supabase | Authentication, database, file storage | Account credentials, application data, audit events | AWS us-east-1 (default) |
| Netlify | Web hosting, edge CDN, serverless functions | Request logs (IP, user agent), function invocations | US + global edge |
| Cloudflare | DNS resolution only (no proxy) | DNS query logs (standard) | Global |
| Transactional email provider | Brief download link delivery, contact confirmation | Name, email address, download intent | Configured per deployment |
For questions about sub-processor agreements or data processing agreements (DPAs), contact privacy@strataview.earth.
See also: Privacy Policy · Terms of Service