Architecture
Gerbtrace uses a client-rendered Nuxt application and a Tauri desktop app that bundles the same application frontend. Public marketing and documentation routes render and prerender HTML on the web (ADR-012); authenticated application routes remain client-rendered. Collaboration features are backed by Supabase Cloud.
High-level components
The system consists of three main deployment targets:
- Web app – hosted on Cloudflare Workers with public HTML and client-rendered application routes; the Worker serves static assets, a small set of Nitro API routes, and docs content from D1
- Desktop app – Tauri 2 (Rust) wrapping the same Nuxt frontend, distributed via GitHub Releases
- API server – Supabase Cloud (PostgreSQL, Storage, Realtime, Edge Functions, Auth)
The browser/desktop client communicates with the API server over HTTPS for data operations and WSS for real-time subscriptions. The desktop app additionally checks GitHub Releases for auto-updates.
Architecture decisions
Architecture Decision Records (ADRs) live in docs/adr/. Start with
docs/adr/README.md for the central decision log, status taxonomy, accepted
decisions, and proposed roadmap decisions. When this overview conflicts with an
ADR, the ADR is the source of truth for the decision it covers.
Gerber rendering pipeline
Raw Gerber and drill files are processed through a multi-stage pipeline:
- Tokenizer – raw Gerber/drill text is split into tokens
- Parser – tokens are parsed into a typed AST (Abstract Syntax Tree) of Gerber commands
- Plotter – the AST is transformed into an
ImageTreeof drawing primitives (arcs, lines, regions, flashes) - Renderer – the
ImageTreeis drawn onto an HTML5 Canvas element
The pipeline lives in lib/gerber/ (tokenizer, parser, plotter) and lib/renderer/ (canvas renderer, realistic renderer, SVG exporter, pixel diff).
Renderer variants
| Renderer | Location | Purpose |
|---|---|---|
| Canvas 2D | lib/renderer/canvas-renderer.ts | Standard layer rendering |
| Realistic | lib/renderer/realistic-renderer.ts | 3D-style PCB appearance |
| SVG Exporter | lib/renderer/svg-exporter.ts | SVG export |
| DXF Exporter | lib/renderer/dxf-exporter.ts | DXF export for CAD integration |
| Pixel Diff | lib/renderer/pixel-diff.ts | Comparison mode pixel differencing |
Data persistence
| Data | Local (browser/desktop) | Team (cloud) |
|---|---|---|
| Projects | IndexedDB via Dexie.js | Supabase Postgres |
| Files | IndexedDB via Dexie.js | Supabase Storage |
| Packages | Static JSON (public/packages/) | Supabase JSONB (team_packages) |
| THT Packages | Static JSON (public/packages/tht-libraries/) | Supabase JSONB (team_tht_packages) |
| User preferences | localStorage | – |
Local projects work entirely offline. A successfully opened team project and its downloaded files are cached in account-scoped IndexedDB, so they can be reopened without a connection and edits queue in the durable outbox. Live collaboration, authentication refreshes, AI enrichment, supplier pricing, and the first load of uncached team data still require the Supabase/API connection.
Team project updates are measured at the viewer persistence boundary with
app/utils/project-payload-metrics.ts. The metrics warn when a single update or
field approaches the ADR-006 row-growth thresholds, which informs future
extraction of large/hot feature data into child tables. BOM lines and pricing
now use independent project_bom_data child rows.
Package system
Built-in packages are organized as a library tree under public/packages/libraries/. At runtime, the library tree manifest (_tree.json) is loaded, and individual packages are fetched on demand.
Team packages are stored in Supabase as JSONB and merged into the in-app library at runtime. Export to .pck uses the TPSys serializer in app/utils/pck-serializer.ts.
All packages use TPSys package types exclusively. See Package Definitions for the type system.
Collaboration and sync
- Auth – Supabase Auth with Microsoft OAuth (Entra ID), GitHub OAuth, email/password, and magic link flows
- Teams and projects – stored in Postgres, shared via Supabase Realtime subscriptions
- Spaces – project and package scoping within teams, with guest access via space invitations
- Conversations – threaded project discussions with mentions, references, and file attachments
- Notifications – real-time notification delivery for mentions, approvals, and status changes
- Presence – tracked via Realtime channels to show who is viewing a project
- Files – stored in Supabase Storage (
gerber-filesbucket), metadata tracked in Postgres - Row-level security – all tables use RLS policies that enforce team membership, role-based access, and space-level scoping
Database schema
The database is defined through idempotent SQL migrations in supabase/migrations/. Key tables:
| Table | Purpose |
|---|---|
profiles | User profiles with avatar and display name (auto-created on signup) |
teams | Team definitions with subdomain slugs and settings |
team_members | User-team relationships with roles (admin, editor, viewer, guest) |
team_invitations | Pending email invitations |
spaces | Workgroups within teams for organizing projects |
space_members | User-space membership |
space_invitations | Guest invitations to spaces with expiry tokens |
projects | PCB projects with PnP, panel, paste, and pcb_data JSONB. BOM lines and pricing live in project_bom_data |
project_bom_data | Partitioned BOM payloads keyed by kind (lines or pricing_cache), each an independent realtime row |
project_files | Gerber/drill file metadata |
project_documents | PDF document metadata |
project_conversation_messages | Threaded conversation messages per project |
project_conversation_attachments | File attachments on conversation messages |
notifications | User notifications (mentions, approvals, status changes) |
bom_groups | Named groups for organizing BOM lines |
team_packages | Custom SMD package definitions |
team_tht_packages | Custom THT package definitions |
Edge functions
Supabase Edge Functions (Deno runtime) handle server-side operations:
| Function | Purpose |
|---|---|
admin-password-reset | Admin-initiated password resets |
send-invitation | Send team and space guest invitation emails (via Resend API) |
elexess-exchange-rate | Daily USD→EUR rate for BOM pricing, proxied from the ECB reference feed (CORS bypass) |
main/ai-enrich-bom | Spark AI BOM enrichment |
main/ai-validate-key | Validate Spark AI provider keys (platform admins only) |
main/elexess-search | Elexess part search through api.elexess.com with the team's or the platform's Elexess API token, mapped to the cached pricing shape |
main/elexess-validate-credentials | Validate an Elexess API token against the quota-free suppliers endpoint (platform admins only) |
stripe-* | Stripe checkout, portal, and webhook handling |
admin-* | Platform admin usage and team actions |
delete-account | Account deletion workflow |
telemetry | Lightweight telemetry endpoint |
API routes
Supabase Edge Functions are the canonical server-side API for business logic (ADR-001). Nuxt server API routes are reserved for operations that must live on the app's own origin:
| Route | Purpose |
|---|---|
/api/legal/consent | Record legal consent on the app origin |
/api/account/export | Export account data as a same-origin download |
Frontend structure
The Nuxt app follows a composable-driven architecture:
- Pages (
app/pages/) – route definitions for viewer, compare, team, auth, docs, inbox, and spaces - Components (
app/components/) – organized by feature area (viewer, compare, import, packages, docs, shared) - Composables (
app/composables/) – state management and business logic (useProject, useBom, usePickAndPlace, useAuth, useTeamProjects, useSpaces, useProjectConversation, useNotifications, useDrawTool, useAiEnrichment, useErrorReporting, etc.) - Utils (
app/utils/) – pure functions for parsing, geometry, formatting, and conventions - Libraries (
lib/) – rendering pipeline, Gerber/drill generation, jet print export, DXF export