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:
| Concern | RoundTrip | Waypoint |
|---|---|---|
| Primary domain | Field service management | Business operations management |
| Real-time requirements | High (SignalR dispatch board) | Low (dashboard refreshes on load) |
| Offline requirements | High (technician PWA) | None (desktop-first) |
| External integrations | Entra, Stripe, SendGrid, Graph API | Entra, Stripe, SendGrid, Command Center API, Azure Blob |
| Data sources | Own database only | Own database + RoundTrip (via Command Center API) |
| File storage | Invoice PDFs only | General 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
| Property | Value |
|---|---|
| Framework | React 19, Vite, TypeScript |
| Styling | Tailwind CSS v4 |
| State — server | TanStack Query v5 |
| State — UI | Zustand |
| Auth | MSAL (@azure/msal-browser) |
| Charts | Recharts |
| Rich text | TipTap |
| File uploads | Azure Blob Storage via pre-signed URLs |
| Icons | Phosphor Icons |
| Hosting | Cloudflare Pages (waypoint project) |
| Domain | traxswaypoint.app |
| Dev domain | dev.traxswaypoint.app |
| Repo | Traxs-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
| Property | Value |
|---|---|
| Runtime | .NET 10 |
| API framework | FastEndpoints |
| Mediator | Cyrus (Mediator.SourceGenerator) |
| ORM | EF Core (writes) + Dapper (reads) |
| Background jobs | Hangfire |
| Auth | Microsoft Entra External ID (JWT validation) |
| File storage | Azure Blob Storage SDK |
| SendGrid (via Command Center API or direct) | |
| Hosting | Azure App Service (Linux, .NET 10) |
| Domain | api.traxswaypoint.app |
| Repo | Traxs-dev/WaypointAPI |
3.3 Database — Waypoint DB
| Property | Value |
|---|---|
| Engine | Azure SQL Server |
| Database name | WaypointDb |
| Resource name | sqldb-waypoint-production |
| Resource group | rg-waypoint-production |
| Region | Central US (same as RoundTrip) |
| Access | Connection string via Key Vault reference |
| Migrations | Manual (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
| Property | Value |
|---|---|
| Storage account | strwaypoint{env} |
| Container | waypoint-documents |
| Access | Private — accessed via pre-signed URLs only |
| File types | PDF, Word, Excel, PowerPoint, images, CSV, text |
| Max file size | 50MB per file (v1) |
| Retention | Retained 90 days post-cancellation then purged |
Upload flow:
- Frontend requests pre-signed upload URL from Waypoint API
- Waypoint API generates Azure Blob SAS URL (write permission, 5-minute expiry)
- Frontend uploads file directly to Azure Blob (bypasses API — no file through server)
- Frontend confirms upload to Waypoint API
- Waypoint API records document metadata in database
Download flow:
- Frontend requests download URL from Waypoint API
- Waypoint API validates tenant access, generates SAS URL (read permission, 60-minute expiry)
- 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
| Resource | Name | Notes |
|---|---|---|
| Resource Group | rg-waypoint-production | All Waypoint production resources |
| App Service Plan | asp-waypoint-production | Linux, B1 initially |
| App Service | app-waypoint-production | .NET 10, Linux |
| SQL Server | sql-waypoint-production | Shared SQL server instance |
| SQL Database | WaypointDb | Separate database |
| Key Vault | kv-waypoint-production | All secrets |
| Storage Account | strwaypointproduction | Document storage |
| Application Insights | appi-waypoint-production | Monitoring |
6.2 Key Vault Secrets
| Secret Name | Purpose |
|---|---|
ConnectionStrings--Default | Waypoint database connection |
ConnectionStrings--BlobStorage | Azure Blob Storage connection |
AzureAd--ClientId | Waypoint API app registration |
AzureAd--ClientSecret | Waypoint API client secret |
AzureAd--TenantId | roundtripapp tenant ID |
SendGrid--ApiKey | Email delivery |
CommandCenter--ApiKey | Service-to-service auth with Command Center API |
Stripe--SecretKey | Billing |
Stripe--WebhookSigningSecret | Stripe webhook validation |
Stripe--PriceId--Starter | Waypoint Starter plan price ID |
Stripe--PriceId--Standard | Waypoint Standard plan price ID |
Stripe--PriceId--Professional | Waypoint Professional plan price ID |
6.3 Cloudflare Pages
| Property | Value |
|---|---|
| Production project | waypoint |
| Production branch | main |
| Production domain | traxswaypoint.app |
| Dev project | waypoint-dev |
| Dev branch | development |
| Dev domain | dev.traxswaypoint.app |
6.4 ADO Pipelines
Same pattern as RoundTrip:
| Pipeline | Trigger | Deploys To |
|---|---|---|
azure-pipelines.yml (API) | Push to main | app-waypoint-production |
azure-pipelines-dev.yml (API) | Push to development | app-waypoint-dev |
azure-pipelines.yml (Web) | Push to main | Cloudflare Pages waypoint |
azure-pipelines-dev.yml (Web) | Push to development | Cloudflare Pages waypoint-dev |
7. Security
7.1 Authentication
- Microsoft Entra External ID CIAM — same
roundtripapptenant as RoundTrip (ADR-009) - JWT bearer token validation on every API request
TenantScopingBehaviorresolves 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
| Property | Production | Development |
|---|---|---|
| Frontend URL | traxswaypoint.app | dev.traxswaypoint.app |
| API URL | api.traxswaypoint.app | api-dev.traxswaypoint.app |
| Database | WaypointDb (production) | WaypointDb (dev) |
| App Service | app-waypoint-production | app-waypoint-dev |
| Key Vault | kv-waypoint-production | kv-waypoint-dev |
| ASPNETCORE_ENVIRONMENT | Production | Production |
| Branch | main | development |
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
| Tool | Purpose |
|---|---|
| Application Insights | API traces, exceptions, performance, dependency calls |
| Hangfire Dashboard | Background job status (notification emails, etc.) |
| Azure App Service Log Stream | Live container logs for debugging |
| Cloudflare Analytics | Frontend traffic and performance |
| Crisp | Customer support — same workspace as RoundTrip, tagged by product |
11. Key Architecture Decisions
| Decision | Choice | Reference |
|---|---|---|
| Database separation | Separate WaypointDb — no shared schema with RoundTrip | ADR-008 |
| Authentication | Shared roundtripapp Entra tenant — one login for suite | ADR-009 |
| Product architecture | Independent deployable module of Traxs One Suite | ADR-010 |
| RoundTrip data access | Via Command Center API only — never direct DB access | ADR-007 |
| Document storage | Azure Blob — direct upload from browser via SAS URL | This document |
| Real-time | Not required in v1 — no SignalR | This document |
| PWA/offline | Not required — desktop-first product | This document |
| Rich text | TipTap for Knowledge Base editor | This document |
12. Document History
| Version | Date | Author | Changes |
|---|---|---|---|
| 1.0 | July 2026 | Pete Carroll | Initial draft |