Documentation/Architecture/Technical Overview

Technical Architecture

How TrueConfig works under the hood: system design, data flow, and core components.

System Overview

TrueConfig is a continuous governance platform that monitors Microsoft 365 tenants for security misconfigurations and unauthorized changes. It operates by periodically scanning your Microsoft environment, comparing the current state against security baselines (DSC - Desired State Configuration), and detecting drift from the desired state.

text
┌─────────────────────────────────────────────────────────────┐
│                     Microsoft 365                            │
│  (Users, Admins, Security Policies, Applications)           │
└─────────────────────────┬───────────────────────────────────┘
                          │
                          │ Graph API (OAuth 2.0)
                          ↓
┌─────────────────────────────────────────────────────────────┐
│                  TrueConfig Edge Function                      │
│  sync-tenant (Deno runtime, 8-phase scan)                   │
│  ├─ Phase 1-4: Fetch & Store Raw Data                       │
│  ├─ Phase 5: Create Security Snapshot                       │
│  ├─ Phase 6: Detect Configuration Drift                     │
│  ├─ Phase 7: Run DSC Control Evaluations                    │
│  └─ Phase 8: Finalize & Notify                              │
└─────────────────────────┬───────────────────────────────────┘
                          │
                          ↓
┌─────────────────────────────────────────────────────────────┐
│                   Supabase PostgreSQL                        │
│  ├─ tenant_* tables (scan data)                             │
│  ├─ dsc_evaluations (control results)                       │
│  ├─ drift_events (change detection)                         │
│  ├─ snapshots (state backups)                               │
│  └─ audit_log (immutable event log)                         │
└─────────────────────────┬───────────────────────────────────┘
                          │
                          │ RLS-protected queries
                          ↓
┌─────────────────────────────────────────────────────────────┐
│                     Next.js App                              │
│  ├─ Server Components (data fetch via getContext)           │
│  ├─ Server Actions (mutations)                              │
│  └─ Client Components (UI interactivity)                    │
└─────────────────────────────────────────────────────────────┘

Core Components

Edge Functions (Deno)

Stateless serverless functions that run the scanning logic. Primary function issync-tenantwhich orchestrates the 8-phase scan process.

Location: supabase/functions/sync-tenant/

PostgreSQL Database

Supabase-hosted Postgres with Row-Level Security (RLS) policies enforcing multi-tenant data isolation. All queries automatically filtered by organization_id.

Schema: supabase/migrations/

DSC Evaluation Engine

Control evaluation system that compares scan data against baseline security controls. Each control has an evaluator function that returns pass/fail/warning status.

Engine: src/lib/dsc/engine.ts

Microsoft Graph Integration

OAuth 2.0 authenticated API calls to Microsoft Graph v1.0. Refresh tokens stored encrypted at-rest, rotated every 90 days per Microsoft best practices.

Tokens: tenant_connections table

Data Flow: Scan to Dashboard

1. Scan Trigger

Scans can be triggered manually (user clicks "Scan Now"), on a schedule (cron-based), or via API. The scan creates a new scan_id UUID and updates the tenant status to "scanning".

2. Data Collection (Phases 1-4)

Edge function makes parallel API calls to Microsoft Graph endpoints to fetch:

  • Users (GET /users with $select for specific fields)
  • Directory roles and role assignments
  • Groups and group memberships
  • Service principals and app registrations
  • Conditional Access policies
  • MFA registration status (if available)

Data is normalized and stored in tenant_* tables tagged with the current scan_id. Old scan data is retained per plan limits (30 days / 1 year / unlimited).

3. Snapshot Creation (Phase 5)

A complete snapshot of security-critical configuration (Conditional Access policies, role assignments, security defaults) is serialized to JSONB and stored in the snapshots table. This enables point-in-time comparison for drift detection.

4. Drift Detection (Phase 6)

Current snapshot is compared against the previous snapshot using PostgreSQL JSONB operators:

sql
-- Simplified drift detection query
SELECT
  jsonb_path_query(
    current_snapshot - previous_snapshot,
    '$.conditionalAccessPolicies[*] ? (@.state != "disabled")'
  ) AS changed_policies
FROM snapshots
WHERE scan_id = current_scan_id;

Detected changes are recorded as drift_events with actor attribution from Microsoft audit logs when available.

5. Control Evaluation (Phase 7)

For each enabled control in the tenant's baseline:

  1. Load control definition and evaluator function
  2. Query scan data (e.g., SELECT * FROM tenant_users WHERE is_mfa_enabled = false)
  3. Execute evaluator logic (compare expected vs actual state)
  4. Generate result object with status, severity, evidence, resource counts
  5. Upsert to dsc_evaluations table (composite key: tenant_id, scan_id, control_id)
  6. Record audit event via record_dsc_event() RPC function

6. Dashboard Display

Web app queries aggregated statistics on page load:

sql
-- Deviation summary calculation
SELECT
  COUNT(*) FILTER (WHERE status = 'pass') as passed,
  COUNT(*) FILTER (WHERE status = 'fail' AND severity = 'critical') as critical,
  COUNT(*) FILTER (WHERE status = 'fail' AND severity = 'high') as high,
  COUNT(*) FILTER (WHERE status = 'fail' AND severity = 'medium') as medium,
  COUNT(*) FILTER (WHERE status = 'fail' AND severity = 'low') as low
FROM dsc_evaluations
WHERE tenant_id = $1
  AND scan_id = (SELECT MAX(scan_id) FROM tenant_scans WHERE tenant_id = $1);

Results are computed on-the-fly (no pre-aggregated statistics stored) and cached at the Next.js request level using React cache().

Multi-Tenancy & Data Isolation

TrueConfig uses a strict hierarchical multi-tenancy model: User → Organization → Tenants. Data isolation is enforced at multiple layers:

Database Level (RLS)

Every table has Row-Level Security policies that automatically filter queries by organization_id:

sql
-- Example RLS policy on tenant_users table
CREATE POLICY "Users can only access their org's tenant data"
ON tenant_users
FOR ALL
USING (
  organization_id = (
    SELECT organization_id FROM user_memberships
    WHERE user_id = auth.uid()
  )
);

Application Level (Context)

All server components and actions use getContext() which loads and caches the user's organization context, permissions, and plan limits. Failed context checks return 403 errors.

typescript
// Every protected route starts with:
const context = await requireContext();
// context contains: user, organization, membership, plan, permissions

// All queries automatically scoped:
const { data } = await supabase
  .from('tenants')
  .select('*')
  .eq('organization_id', context.organization.id); // RLS enforces this

API Level (Token Scoping)

Microsoft Graph API tokens are scoped per-tenant. Refresh tokens stored encrypted intenant_connections with unique tenant_ms_id foreign key. Cross-tenant access is architecturally impossible.

Isolation Guarantee
Even with direct database access, users cannot query data from other organizations due to RLS policies being enforced at the PostgreSQL level. This provides defense-in-depth against application-level bugs.

Plan Tiers & Feature Gating

TrueConfig offers three plan tiers (Essential, Pro, Scale) with different control sets and capabilities. Plan gating is enforced during control evaluation:

typescript
// In Phase 7 (DSC Evaluation)
const { data: planData } = await supabase
  .from('plan_subscriptions')
  .select('plan_tier')
  .eq('organization_id', organizationId)
  .maybeSingle();

const planTier: PlanTier = planData?.plan_tier || 'essential';

// Filter controls by plan tier
const planOrder = { essential: 1, pro: 2, scale: 3 };
const controlsToRun = enabledControls.filter(c => {
  const controlPlan = c.minimum_plan_tier || 'essential';
  return planOrder[controlPlan] <= planOrder[planTier];
});

// Essential: 15 controls, Pro: 33 controls, Scale: 33 controls

Essential (15 controls)

Foundational identity and privileged access controls. Manual remediation only. 3 scans/day limit. 30-day data retention.

Pro (33 controls)

Full control set including Conditional Access and application governance. One-click remediation. Unlimited scans. 1-year data retention.

Scale (33 controls)

Same controls as Pro with automated remediation, SIEM export, dedicated support, and unlimited data retention.