Architecture

How Trueno is built.

A high-level tour of the stack and the access model. Every layer maps to a real file in the repository; we link to the relevant page when there's more depth available.

Application stack

Frontend — Next.js 16 (App Router)

Server components by default. Every dashboard surface is server-rendered against the active workspace; only a small set of interactive controls (filter chips, status mutation buttons, the command palette) ship as client components.

  • TypeScript strict mode end-to-end
  • Tailwind CSS v4 with workspace-scoped tokens
  • No client-side data store; pages re-render on server actions

Backend — Supabase (Postgres + Auth)

Workspace data lives in Postgres. Authentication is Supabase Auth with magic-link and password flows. Every multi-tenant table carries an organization_id column and a row-level security policy that gates access by the active workspace.

  • Row-level security on every workspace table
  • Service-role key reserved for the async processor and scan runners
  • Migrations versioned in /supabase/migrations

AWS integration

AssumeRole + external ID

Trueno never asks for long-lived AWS keys. Each workspace generates a unique external ID. The IAM trust policy you create in your AWS account names the Trueno principal and the external ID as a condition. On each scan, the platform calls sts:AssumeRole with that external ID and uses the short-lived credentials STS returns.

Read-only by design

The role you create attaches AWS-managed read-only policies — examples: ReadOnlyAccess, SecurityAudit, AmazonEC2ReadOnlyAccess. The Trueno SDK code never invokes a write API. Revoking access is a one-step IAM-role deletion in your AWS console.

Server-side AWS SDK

Every file that imports @aws-sdk/* begins with import "server-only". This guarantees SDK code cannot accidentally end up in a client bundle, and the build fails if a regression tries to leak it.

Async processing

Async job queue

Scans don't run inline with the request that triggered them. Clicking "Run scan" enqueues a row in scan_jobs (status = queued). A separate processor route claims jobs using Postgres SELECT … FOR UPDATE SKIP LOCKED, runs the work, and writes the terminal state. This makes scans race-safe and retryable.

  • max_attempts = 2 with automatic retry on transient failure
  • Locked-by + lock-expires-at recover orphaned jobs
  • Atomic enqueue via a Postgres function that rolls back both inserts

Scheduled jobs

Recurring inventory, cost, and recommendation jobs are tracked in scheduled_jobs. A scheduler endpoint runs on a Vercel Cron trigger, claims due jobs, and dispatches them to the same async queue.

Deployment

Vercel — Next.js host + Cron

The app is deployed to Vercel as a Next.js application. Vercel Cron drives the scheduler and the scan processor on a recurring schedule. The cron config is checked in (/vercel.json) so the schedule lives with the code.

Supabase — database + auth + storage

Postgres database, Supabase Auth, and Supabase Storage are managed by Supabase. Workspace-scoped row-level security policies are enforced at the database layer, not just the application layer.

Data boundaries

Findings stay in your workspace

Findings, resources, scans, recommendations, comments, and assignments are written to your workspace only. There is no cross-tenant aggregation, no "benchmark vs. peers" feature, no training data extracted from your environment.

AWS credentials never persist beyond a single call

The short-lived credentials STS returns are used to fulfill a scan and then discarded. We persist the role ARN and the external ID hash so we can call AssumeRole again. Nothing else.

Telemetry is operational

We collect basic operational telemetry — error rates, scan latencies, job retry counts — to operate the service. We don't sell, share, or train on your data.

Going deeper

The security page covers the AWS access model and isolation guarantees in more depth. The docs page walks through what you actually see during onboarding.