Skip to main content

Waypoint — Solution Architecture Document

Version: 1.0
Date: July 2026
Author: Pete Carroll, Founder — Traxs Group LLC
Status: Draft
Product: Waypoint — Business Operating System


1. Architecture Overview

Waypoint follows the same foundational architecture as RoundTrip — Clean Architecture, Domain-Driven Design, FastEndpoints, Cyrus Mediator, React 19 frontend. Any developer familiar with RoundTrip can navigate Waypoint immediately.

The key differences from RoundTrip:

ConcernRoundTripWaypoint
Primary domainField service managementBusiness operations management
Real-time requirementsHigh (SignalR dispatch board)Low (dashboard refreshes on load)
Offline requirementsHigh (technician PWA)None (desktop-first)
External integrationsEntra, Stripe, SendGrid, Graph APIEntra, Stripe, SendGrid, Command Center API, Azure Blob
Data sourcesOwn database onlyOwn database + RoundTrip (via Command Center API)
File storageInvoice PDFs onlyGeneral document storage (multi-MB files)

2. System Context

┌─────────────────────────────────────────────────────────────────┐
│ USERS │
│ Business Owner · Office Manager · Admin Staff │
└──────────────────────────────┬──────────────────────────────────┘
│ HTTPS

┌─────────────────────────────────────────────────────────────────┐
│ CLOUDFLARE │
│ DNS · CDN · Security Headers · Zero Trust │
│ traxswaypoint.app │
└──────────────────────────────┬──────────────────────────────────┘

┌────────────────────┼────────────────────┐
│ │ │
┌─────────▼──────────┐ ┌───────▼──────────┐ ┌──────▼───────────┐
│ REACT FRONTEND │ │ WAYPOINT API │ │ ENTRA EXT ID │
│ Cloudflare Pages │ │ Azure App Svc │ │ CIAM │
│ React 19 · Vite │ │ .NET 10 │ │ roundtripapp │
│ TanStack Query │ │ FastEndpoints │ │ tenant │
│ MSAL · Tailwind │ │ Cyrus Mediator │ └──────────────────┘
└────────────────────┘ └───────┬──────────┘

┌─────────────────────┼──────────────────────┐
│ │ │
┌────────▼───────┐ ┌──────────▼──────────┐ ┌───────▼──────────┐
│ AZURE SQL │ │ AZURE BLOB │ │ COMMAND CENTER │
│ Waypoint DB │ │ STORAGE │ │ API │
│ (separate │ │ waypoint-documents │ │ api.traxsgroup │
│ from RT) │ │ container │ │ .com │
└────────────────┘ └─────────────────────┘ └───────┬──────────┘

┌────────▼─────────┐
│ ROUNDTRIP API │
│ api.roundtrips │
│ .app │
└──────────────────┘

3. Solution Components

3.1 Frontend — Waypoint Web

PropertyValue
FrameworkReact 19, Vite, TypeScript
StylingTailwind CSS v4
State — serverTanStack Query v5
State — UIZustand
AuthMSAL (@azure/msal-browser)
ChartsRecharts
Rich textTipTap
File uploadsAzure Blob Storage via pre-signed URLs
IconsPhosphor Icons
HostingCloudflare Pages (waypoint project)
Domaintraxswaypoint.app
Dev domaindev.traxswaypoint.app
RepoTraxs-dev/WaypointWeb

Key differences from RoundTripWeb:

  • No PWA / service worker — Waypoint is desktop-first, no offline requirement
  • No SignalR — no real-time push needed in v1
  • No Leaflet/map — no geographic data in v1
  • TipTap added for Knowledge Base rich text editor

3.2 Backend — Waypoint API

PropertyValue
Runtime.NET 10
API frameworkFastEndpoints
MediatorCyrus (Mediator.SourceGenerator)
ORMEF Core (writes) + Dapper (reads)
Background jobsHangfire
AuthMicrosoft Entra External ID (JWT validation)
File storageAzure Blob Storage SDK
EmailSendGrid (via Command Center API or direct)
HostingAzure App Service (Linux, .NET 10)
Domainapi.traxswaypoint.app
RepoTraxs-dev/WaypointAPI

3.3 Database — Waypoint DB

PropertyValue
EngineAzure SQL Server
Database nameWaypointDb
Resource namesqldb-waypoint-production
Resource grouprg-waypoint-production
RegionCentral US (same as RoundTrip)
AccessConnection string via Key Vault reference
MigrationsManual (same pattern as RoundTrip — never at startup)

Completely separate from RoundTrip database. No shared tables, no cross-database queries. See ADR-008.

3.4 Document Storage — Azure Blob Storage

PropertyValue
Storage accountstrwaypoint{env}
Containerwaypoint-documents
AccessPrivate — accessed via pre-signed URLs only
File typesPDF, Word, Excel, PowerPoint, images, CSV, text
Max file size50MB per file (v1)
RetentionRetained 90 days post-cancellation then purged

Upload flow:

  1. Frontend requests pre-signed upload URL from Waypoint API
  2. Waypoint API generates Azure Blob SAS URL (write permission, 5-minute expiry)
  3. Frontend uploads file directly to Azure Blob (bypasses API — no file through server)
  4. Frontend confirms upload to Waypoint API
  5. Waypoint API records document metadata in database

Download flow:

  1. Frontend requests download URL from Waypoint API
  2. Waypoint API validates tenant access, generates SAS URL (read permission, 60-minute expiry)
  3. Frontend opens SAS URL directly

3.5 Command Center API Integration

Waypoint calls the Command Center API for all RoundTrip data. The Command Center API calls RoundTrip API internally. Waypoint never calls RoundTrip directly.

Waypoint API
→ POST https://api.traxsgroup.com/v1/roundtrip/revenue
→ Command Center API authenticates service-to-service
→ Command Center API calls RoundTrip API
→ Returns RevenueSnapshot DTO
→ Waypoint caches in Redis or in-memory (15 min TTL)

Service-to-service auth: Command Center API validates requests from Waypoint using a shared API key stored in Key Vault. Not exposed to browser.


4. Solution Architecture — Backend

4.1 Solution Structure

WaypointAPI.sln

├── src/
│ ├── Waypoint.API.SharedKernel/ ← Value objects, base types
│ ├── Waypoint.API.Core/ ← Domain entities, interfaces, enums
│ ├── Waypoint.API.UseCases/ ← Commands, queries, handlers, validators
│ ├── Waypoint.API.Infrastructure/ ← EF Core, Dapper, Blob, Email, Graph
│ └── Waypoint.API.Web/ ← FastEndpoints, Program.cs, middleware

└── tests/
├── Waypoint.API.UnitTests/
├── Waypoint.API.IntegrationTests/
├── Waypoint.API.FunctionalTests/
└── Waypoint.API.Architecture.Tests/

4.2 Layer Dependencies

Same rule as RoundTrip — dependencies only flow inward:

Web → Infrastructure → UseCases → Core → SharedKernel

4.3 Core Domain Areas

Waypoint.API.Core/
Aggregates/
Financial/
RevenueEntry.cs ← Manual revenue entries
Expense.cs ← Expense records
HR/
Employee.cs ← Aggregate root
Certification.cs ← Child entity
LeaveRequest.cs ← Child entity
PerformanceReview.cs ← Child entity
Documents/
DocumentRecord.cs ← File metadata aggregate
KnowledgeArticle.cs ← Knowledge base article
Workspace/
Workspace.cs ← Tenant configuration
ValueObjects/
Money.cs ← Amount + Currency (reuse from SharedKernel)
DateRange.cs ← Start + End date
StoragePath.cs ← Blob storage path
EmployeeId.cs
DocumentId.cs
LeaveRequestId.cs
Enums/
ExpenseCategory.cs
LeaveType.cs
LeaveStatus.cs
EmploymentType.cs
DocumentCategory.cs
ArticleStatus.cs
Interfaces/
IEmployeeRepository.cs
IDocumentRepository.cs
IExpenseRepository.cs
IRevenueRepository.cs
IBlobStorageService.cs
ICommandCenterClient.cs ← RoundTrip data via Command Center API

4.4 Key Use Cases

Waypoint.API.UseCases/Features/
Dashboard/
GetDashboardSummary/ ← Aggregates all widget data
Financial/
GetPLOverview/
AddRevenueEntry/
AddExpense/
GetARaging/ ← From Command Center API
Employees/
GetEmployeeList/
GetEmployeeProfile/
CreateEmployee/
UpdateEmployee/
AddCertification/
AddLeaveRequest/
ApproveLeaveRequest/
Documents/
GetDocumentLibrary/
RequestUploadUrl/ ← Generates Blob SAS URL
ConfirmUpload/ ← Records metadata after upload
GetDocumentDownloadUrl/ ← Generates read SAS URL
CreateKnowledgeArticle/
UpdateKnowledgeArticle/
Workspace/
GetWorkspaceSettings/
UpdateWorkspaceSettings/
ConnectRoundTrip/
GetIntegrationStatus/

4.5 Request Pipeline

Same pipeline as RoundTrip:

FastEndpoint
→ LoggingBehavior
→ ValidationBehavior (FluentValidation)
→ TenantScopingBehavior
→ TransactionBehavior
→ Handler
→ EF Core / Dapper / Blob / Command Center API

4.6 Dashboard Aggregation Pattern

The GetDashboardSummary query is the most complex read in the system. It aggregates data from multiple sources:

public sealed class GetDashboardSummaryHandler
: IRequestHandler<GetDashboardSummaryQuery, Result<DashboardSummaryDto>>
{
// Runs all data fetches in parallel for performance
public async ValueTask<Result<DashboardSummaryDto>> Handle(
GetDashboardSummaryQuery query, CancellationToken ct)
{
var tasks = new[]
{
GetRevenueMtdAsync(ct), // Waypoint DB + Command Center API
GetArAgingAsync(ct), // Command Center API (if RT connected)
GetOpenTicketsAsync(ct), // Command Center API (if RT connected)
GetTeamSummaryAsync(ct), // Waypoint DB
GetUpcomingLeaveAsync(ct), // Waypoint DB
GetExpiringCertsAsync(ct), // Waypoint DB
GetRecentInvoicesAsync(ct), // Command Center API (if RT connected)
};

await Task.WhenAll(tasks);

return Result<DashboardSummaryDto>.Success(new DashboardSummaryDto(
RevenueMtd: await tasks[0],
ArAging: await tasks[1],
OpenTickets: await tasks[2],
TeamSummary: await tasks[3],
UpcomingLeave: await tasks[4],
ExpiringCerts: await tasks[5],
RecentInvoices: await tasks[6]
));
}
}

Parallel execution keeps dashboard load time under 2 seconds even with multiple API calls.


5. Solution Architecture — Frontend

5.1 Folder Structure

src/
app/
AuthProvider.tsx
router.tsx
msalConfig.ts
features/
dashboard/
components/
widgets/
RevenueMtdWidget.tsx
ArAgingWidget.tsx
OpenTicketsWidget.tsx
TeamTodayWidget.tsx
UpcomingLeaveWidget.tsx
ExpiringCertsWidget.tsx
RecentInvoicesWidget.tsx
DashboardPage.tsx
hooks/
useDashboardSummary.ts
financials/
components/
hooks/
pages/
hr/
components/
hooks/
pages/
documents/
components/
hooks/
pages/
team/
components/
hooks/
pages/
settings/
components/
hooks/
pages/
components/
ui/ ← Shared UI components
layout/ ← AppShell, Sidebar, TopBar, SuiteNav
lib/
api/ ← API client functions (per domain)
auth/ ← MSAL config and hooks
queryClient.ts
styles/
global.css

5.2 Suite Navigation Component

The suite switcher is a shared component that all Traxs products will include. In v1 it is built directly into WaypointWeb — in future it becomes a shared npm package.

// components/layout/SuiteNav.tsx
// Shows products the tenant has active subscriptions to
// Calls Command Center API: GET /v1/suite/subscriptions
export function SuiteNav() {
const { subscriptions } = useSuiteSubscriptions()
return (
<div className="suite-nav">
{subscriptions.roundtrip && (
<a href="https://app.roundtrips.app">RoundTrip</a>
)}
<span className="active">Waypoint</span>
{subscriptions.relay && (
<a href="https://traxsrelay.app">Relay</a>
)}
</div>
)
}

5.3 File Upload Pattern

Direct-to-blob upload keeps large files out of the API server:

// lib/api/documents.ts
export async function uploadDocument(file: File, folderId: string) {
// 1. Request pre-signed URL from Waypoint API
const { uploadUrl, documentId } = await apiClient.post(
'/v1/documents/upload-url',
{ fileName: file.name, fileSize: file.size, folderId }
)

// 2. Upload directly to Azure Blob (no server involvement)
await fetch(uploadUrl, {
method: 'PUT',
body: file,
headers: { 'x-ms-blob-type': 'BlockBlob' }
})

// 3. Confirm upload to Waypoint API (records metadata)
await apiClient.post('/v1/documents/confirm-upload', { documentId })
}

6. Infrastructure

6.1 Azure Resources

ResourceNameNotes
Resource Grouprg-waypoint-productionAll Waypoint production resources
App Service Planasp-waypoint-productionLinux, B1 initially
App Serviceapp-waypoint-production.NET 10, Linux
SQL Serversql-waypoint-productionShared SQL server instance
SQL DatabaseWaypointDbSeparate database
Key Vaultkv-waypoint-productionAll secrets
Storage AccountstrwaypointproductionDocument storage
Application Insightsappi-waypoint-productionMonitoring

6.2 Key Vault Secrets

Secret NamePurpose
ConnectionStrings--DefaultWaypoint database connection
ConnectionStrings--BlobStorageAzure Blob Storage connection
AzureAd--ClientIdWaypoint API app registration
AzureAd--ClientSecretWaypoint API client secret
AzureAd--TenantIdroundtripapp tenant ID
SendGrid--ApiKeyEmail delivery
CommandCenter--ApiKeyService-to-service auth with Command Center API
Stripe--SecretKeyBilling
Stripe--WebhookSigningSecretStripe webhook validation
Stripe--PriceId--StarterWaypoint Starter plan price ID
Stripe--PriceId--StandardWaypoint Standard plan price ID
Stripe--PriceId--ProfessionalWaypoint Professional plan price ID

6.3 Cloudflare Pages

PropertyValue
Production projectwaypoint
Production branchmain
Production domaintraxswaypoint.app
Dev projectwaypoint-dev
Dev branchdevelopment
Dev domaindev.traxswaypoint.app

6.4 ADO Pipelines

Same pattern as RoundTrip:

PipelineTriggerDeploys To
azure-pipelines.yml (API)Push to mainapp-waypoint-production
azure-pipelines-dev.yml (API)Push to developmentapp-waypoint-dev
azure-pipelines.yml (Web)Push to mainCloudflare Pages waypoint
azure-pipelines-dev.yml (Web)Push to developmentCloudflare Pages waypoint-dev

7. Security

7.1 Authentication

  • Microsoft Entra External ID CIAM — same roundtripapp tenant as RoundTrip (ADR-009)
  • JWT bearer token validation on every API request
  • TenantScopingBehavior resolves and enforces TenantId on every handler

7.2 Multi-Tenant Isolation

  • EF Core global query filter: HasQueryFilter(e => e.TenantId == _tenantId)
  • All Dapper queries include WHERE TenantId = @TenantId
  • Blob storage paths are prefixed with TenantId: {tenantId}/{documentId}/{filename}
  • Pre-signed URLs generated per-request — short TTL (5 min upload, 60 min download)

7.3 Document Security

  • No public blob access — all documents accessed via time-limited SAS URLs
  • SAS URLs generated server-side after tenant authorization check
  • Download URLs expire after 60 minutes
  • Upload URLs expire after 5 minutes

7.4 CSP Headers

Same pattern as RoundTrip _headers file. Additional entries needed:

  • Azure Blob Storage domain for direct uploads: https://*.blob.core.windows.net
  • TipTap CDN resources if any (verify at build time)

8. Environments

PropertyProductionDevelopment
Frontend URLtraxswaypoint.appdev.traxswaypoint.app
API URLapi.traxswaypoint.appapi-dev.traxswaypoint.app
DatabaseWaypointDb (production)WaypointDb (dev)
App Serviceapp-waypoint-productionapp-waypoint-dev
Key Vaultkv-waypoint-productionkv-waypoint-dev
ASPNETCORE_ENVIRONMENTProductionProduction
Branchmaindevelopment

9. Deployment Pipeline

Same sequential PR flow as RoundTrip:

feature/WAY-NNN-description
→ PR to development
→ Dev pipeline builds + deploys to dev environment
→ Verify on dev.traxswaypoint.app
→ PR development to main
→ Prod pipeline builds + deploys to production
→ Verify on traxswaypoint.app

Ticket prefix: WAY-NNN (separate Linear project from TRA-NNN)


10. Monitoring & Observability

ToolPurpose
Application InsightsAPI traces, exceptions, performance, dependency calls
Hangfire DashboardBackground job status (notification emails, etc.)
Azure App Service Log StreamLive container logs for debugging
Cloudflare AnalyticsFrontend traffic and performance
CrispCustomer support — same workspace as RoundTrip, tagged by product

11. Key Architecture Decisions

DecisionChoiceReference
Database separationSeparate WaypointDb — no shared schema with RoundTripADR-008
AuthenticationShared roundtripapp Entra tenant — one login for suiteADR-009
Product architectureIndependent deployable module of Traxs One SuiteADR-010
RoundTrip data accessVia Command Center API only — never direct DB accessADR-007
Document storageAzure Blob — direct upload from browser via SAS URLThis document
Real-timeNot required in v1 — no SignalRThis document
PWA/offlineNot required — desktop-first productThis document
Rich textTipTap for Knowledge Base editorThis document

12. Document History

VersionDateAuthorChanges
1.0July 2026Pete CarrollInitial draft