Keystone Business Requirements Document
Version: 1.0.
Date: September 2026.
Author: Pete Carroll, Founder — Traxs Group LLC.
Status: Draft (first real BRD. Everything before this document was framing and a spike, not a product plan).
Product: Keystone: Traxs Group Platform Identity Service
URL: identity.traxsgroup.com (proposed — not yet provisioned).
Where this comes from
Keystone has no BRD/PRD/SAD until now because it didn't need one — it was a decision being framed (keystone-framing.md) and then a spike proving the decision was buildable (keystone-spike-scope.md, keystone-spike-results.md). Both are done. The spike proved all six of its proof items against real evidence, backed by 69 automated tests, at a real, lived cost of ~25 hours. This document is what comes after: turning a proven spike into an actual product plan, scoped to what Keystone needs to become — not just what it was built to prove was possible.
Full production tier, by explicit decision, 2026-09-06. Pete chose to scope this BRD (and the paired PRD/SAD) against the complete production-tier vision — MFA, rate-limiting, audit logging, session revocation, the admin app's identity-administration surface — rather than stopping at the narrower "ready to use" bar. The sequencing already settled in keystone-framing.md §8 (Waypoint and Relay adopt first, RoundTrip migrates last, once there's real production mileage) is unchanged by this — planning for the full tier now doesn't mean building all of it before anything ships.
What changed to make this the right time: Command Center's GET /v1/entitlements is now live, real, and tested (command-center-framing.md §8, done 2026-09-05) — the one dependency Keystone's entitlement delegation needed and didn't have when the spike stubbed it as a hardcoded "yes." That gap is now closeable for real, not just in theory.
Executive Summary
Keystone is the identity backbone of the Traxs Group platform — a self-hosted, OpenIddict-based service that owns credentials (a password hash or a linked external identity) and a stable person ID, shared across RoundTrip, Waypoint, and Relay. It is not a customer-facing product. It has no UI beyond its own login/registration/account-recovery pages. It is infrastructure, in the same sense Command Center is infrastructure — the thing that makes three independent products share one login instead of three.
Keystone replaces RoundTrip's current dependence on Azure Entra External ID, which has two confirmed, real bugs (TRA-384, TRA-389) rooted in a structural property of Entra's global identity graph, not anything fixable at the application layer alone. Keystone's core architectural principle, proven in the spike: Microsoft's token proves who; Keystone's own data decides what. Entra becomes one optional federation choice ("Sign in with Microsoft"), never the required default path.
Entitlement — which products a person can actually reach — is explicitly not Keystone's own data. Keystone delegates that question to Command Center on every check, per the three-piece split settled in keystone-framing.md §9: Keystone owns who, Command Center owns what they're allowed to reach and what they're paying for.
Problem Statement
The Cross-Tenant Collision Problem
Entra External ID is not a standalone product — it's Entra ID wearing a consumer-facing hat, still fundamentally part of Microsoft's broader identity graph. Two real, confirmed bugs follow directly from this:
- TRA-384 — registration fails outright if a prospect's email already has any Entra identity (a real M365 account under that address), because
ProvisionTenantJobused the guest-invitation path. - TRA-389 — even a completely fresh email fails differently: if the domain is verified anywhere on a real Microsoft tenant, Entra's Home Realm Discovery redirects the user to authenticate against that tenant's real credentials instead of RoundTrip's own login — a prompt that looks like a legitimate Microsoft login, which makes the failure mode worse than an obvious error.
Both tickets are closed via a near-term fix (CreateMemberUserAsync, verified empirically). That fix protects RoundTrip today. It does not answer whether Entra is the right long-term foundation for a three-product platform, since the underlying cause — Entra's cross-tenant recognition, a genuine feature from Microsoft's perspective — doesn't go away.
The Multi-Product Entitlement Problem
RoundTrip, Waypoint, and Relay are each independently subscribable. A customer might have RoundTrip alone, RoundTrip and Relay, or all three. "Shared login" therefore cannot mean "one login, same access everywhere" — it has to mean one identity, with per-account, per-product entitlement determining what that identity can actually reach. No existing system does this today; each product would otherwise reinvent it.
The Centralization-Risk Problem
Today, if RoundTrip's Entra integration has a problem, Waypoint is completely unaffected — they're independent systems. A single Keystone service becomes a single point of failure for the entire platform at once: an outage or a security flaw doesn't degrade one product, it takes down or exposes all of them simultaneously. This is not a reason to avoid building it — most successful multi-product platforms are built exactly this way — but it means the bar for "done" is materially higher than for any one product's auth today, and that bar needs to be explicit rather than assumed. Section 5 sets it out in tiers for exactly this reason.
Business Objectives
| Objective | Metric | Target |
|---|---|---|
| Eliminate the Entra cross-tenant collision class of bug platform-wide | Zero HRD-redirect or guest-collision incidents across any product | Ongoing, from Keystone's first real cutover |
| One login across all subscribed products | A customer with 2+ products authenticates once, reaches all of them | Ready-to-use tier |
| Real entitlement delegation to Command Center | Every product access decision reflects live subscription state, not a stub | Ready-to-use tier — unblocked 2026-09-05 |
| Nobody permanently locked out | A working, tested password-reset flow | Ready-to-use tier |
| Stable person ID other products can build against | RoundTrip and Waypoint FK-compatible with zero structural changes | Proven in spike (2026-08-22) |
| Meaningful defense against credential-stuffing / brute-force | Two-layer rate limiting — a moderate per-IP limit plus a stricter per-account lockout after repeated failures, regardless of source IP (§8 item 3, confirmed direction, specific numbers still open) | Production tier |
| A real second factor available | MFA via TOTP (authenticator app), confirmed as the method (§8 item 1); enrollment trigger (mandatory/optional/tenant-admin-controlled) still open, flagged for real discussion | Production tier |
| Every security-relevant action is traceable | Audit log covering login, password change, account linking/unlinking, admin actions, retained 1 year minimum (§8 item 2, confirmed) | Production tier |
| A compromised session or token can be killed | Real session/token revocation — both self-service ("log out everywhere") and admin-app action (§8 item 5, confirmed) | Production tier |
| The platform's identity layer is administrable without touching a database directly | Admin app can view/search accounts, unlink external logins, force password reset, view audit log | Production tier |
| RoundTrip's eventual cutover doesn't cost a single customer their access | Tested account-recovery flow, rollback path, user communication ready before cutover | RoundTrip migration gate (keystone-framing.md §8) |
Stakeholders
| Role | Person / System | Interest |
|---|---|---|
| Owner / Architect | Pete Carroll | All decisions |
| COO / CMO | Robin Carroll | All descisions |
| Consumer: RoundTrip | RoundTrip API | Migrates last, per the settled rollout sequencing; carries the only real customer-facing migration risk |
| Consumer: Waypoint | Waypoint API | Adopts early — real technical Entra dependency to unwind, but zero paying customers to disrupt |
| Consumer: Relay | Relay API (not yet built) | Builds against Keystone from day one — no legacy Entra dependency to migrate away from |
| Dependency: Command Center | Command Center API | Keystone calls Command Center's GET /v1/entitlements on every access decision; Keystone owns no entitlement data itself |
| Consumer: Admin App | The unified admin app (not yet built) | Identity administration (account search, unlink, force-reset, audit log) — authenticated through Keystone itself, per keystone-spike-scope.md §1 |
| External dependency: Microsoft Entra | Optional federation provider | "Sign in with Microsoft" only — never the required default path |
Scope
Proven in the spike (2026-08-22 through 2026-09-01) — not a plan, already built and tested
| Capability | Status |
|---|---|
| OpenIddict token issuance, full interactive authorization-code flow | ✅ Proven — genuine encrypted tokens, spec-correct rejection on bad credentials |
| Email/password registration and login | ✅ Proven |
| Optional Entra federation ("Sign in with Microsoft") against a real app registration | ✅ Proven against real Microsoft infrastructure |
ExternalLogins linking — all three cases (already-linked, existing-account conflict, brand-new registration) | ✅ Proven, with guardrails: verified-email-required linking, no silent auto-merge |
Stable person ID, FK-compatible with RoundTrip's TenantUsers/Technician | ✅ Confirmed against real RoundTrip source, zero structural changes needed |
Same FK pattern against Waypoint's WaypointUsers | ✅ Confirmed against real Waypoint source, genuinely simpler than RoundTrip's case |
69 automated tests back this. This is spike-quality code proving mechanics work, not production-hardened infrastructure — see Section 5.2 for what closes that gap.
Ready-to-use tier (needed now — the actual next work)
| Capability | Why it's needed now |
|---|---|
| Real password reset flow | The spike's own named gap against keystone-framing.md §8's "ready to use" floor — "nobody gets permanently locked out." Not built at all today. |
| Real entitlement delegation to Command Center | The spike stubbed this as a hardcoded "yes." Command Center's GET /v1/entitlements is now live and tested — this is a real integration to build, not a design question. |
| Harden the spike codebase into a deployable service | The spike proved mechanics on a demo project. A real service needs real configuration management, real secrets handling (Key Vault, not local appsettings), real logging, and a real deployment pipeline — none of which was in the spike's scope. |
| Basic input validation and error handling to production standard | Matches the pattern already established in Command Center (ValidationBehavior, GlobalExceptionMiddleware) — Keystone should follow the same Clean Architecture conventions RoundTrip and Command Center already use. |
Production tier (planned now, built incrementally — per Pete's 2026-09-06 scope decision)
| Capability | Description |
|---|---|
| Multi-factor authentication | TOTP (authenticator-app) as the baseline second factor. See PRD for the specific proposed flow — this is a genuine design proposal, not yet a settled decision. |
| Rate limiting / brute-force protection | Login and password-reset endpoints specifically — the endpoints Entra currently protects for free that a self-hosted system has to build itself. |
| Real audit logging | Every security-relevant event (login success/failure, password change, external-login link/unlink, admin action) — who, what, when, from where. |
| Session / token revocation | A compromised refresh token or session must be killable on demand, not just left to expire naturally. |
| Uptime monitoring and alerting | Given Section 2.3's centralization risk — an identity outage takes the whole platform down, so it needs the platform's most aggressive monitoring, not its least. |
| Tested backup/recovery for the credential store | The one dataset where "restore from yesterday's backup" is not an acceptable answer to "we lost the database." |
| Admin app — identity administration surface | Account search, view linked external logins, force a password reset, unlink an external login, view a user's audit trail. This is the Keystone-facing half of the admin app named in keystone-spike-scope.md §1 — the Command Center-facing half (ops/financial dashboard) is tracked separately in command-center-framing.md §8. |
RoundTrip migration (separate, gated, last — per keystone-framing.md §8)
Not "ready to use" or "production tier" scope — a distinct, time-sensitive, one-time cutover with its own explicit gate: a tested (not just built) account-recovery flow, a real rollback path, and the "set up your new login" user communication designed and ready. RoundTrip has real customers today; this is the one migration where a mistake costs an actual customer, which is exactly why it's sequenced last, after Waypoint and Relay have given Keystone genuine production mileage under real (if lower-stakes) traffic.
Explicitly out of scope for Keystone itself
- Entitlement data. Keystone never stores which products a tenant is entitled to, what tier they're on, or pricing. That's Command Center's data, queried live on every check.
- Product-specific user data. RoundTrip's
TenantUsers/Technician, Waypoint'sWaypointUsers— all stay exactly where they are, referencing Keystone's person ID by FK. Keystone never becomes a shared user-profile table. - Billing. Not Keystone's concern at any tier — Command Center's, per the three-piece split.
Non-Functional Requirements
| Requirement | Target | Rationale |
|---|---|---|
| Availability | 99.9% uptime (production tier) | Higher than Command Center's 99.5% — Keystone is a harder single point of failure: every product's every request depends on a valid token, not just cross-product data sharing. |
| Response time | < 300ms for token issuance and validation | Auth checks happen on effectively every authenticated request across every product — this is the platform's hottest path. |
| Security — password storage | PBKDF2-HMAC-SHA256, 600,000 iterations, per current OWASP guidance — proven in spike, matching the real implementation | No ASP.NET Core Identity is in use (per keystone-external-login-proposal.md's stated constraint), so this is Keystone's own responsibility to get right, not inherited from a framework — and it already has been, tested. |
| Security — token lifetime | Short-lived access tokens, longer-lived refresh tokens, both revocable | Standard OAuth2/OIDC practice; revocability is what Section 5.3's session/token revocation requirement actually depends on. |
| Security — transport | TLS everywhere, no exceptions, including local dev where feasible | The Entra-federation callback flow already surfaced a real cross-site cookie/HTTP interaction during the spike (keystone-spike-results.md §3) — this is a real, not theoretical, risk class. |
| Audit log retention | Confirmed: 1 year minimum | Matches PCI-DSS's own 1-year minimum — a genuine industry floor, not a compromise. May warrant a per-customer exception later if a future customer's own regulatory obligations require longer retention (§8 item 2). |
Constraints
- No ASP.NET Core Identity. The spike and the external-login-proposal it's built on both deliberately avoid it, matching RoundTrip's own existing pattern (FastEndpoints, Clean Architecture, no ASP.NET Core Identity). Password hashing, session management, and account linking are Keystone's own code, not inherited from a framework.
- Same Clean Architecture as RoundTrip and Command Center — Core/UseCases/Infrastructure/Web, FastEndpoints + Cyrus Mediator. Any Traxs developer should be able to navigate this codebase immediately.
- Solo developer, evenings-and-weekends pace. The spike's own ~25-hour, 11-session cost is the honest baseline for what "one more real feature" costs at this pace — production-tier items should be scoped with that same honesty, not planned as if a team were building them.
- Never a shared schema with any product. Keystone's own database holds only credentials, external-login links, and the person ID — nothing product-specific, ever.
- Never silent account merging. Proven in the spike and non-negotiable going forward: linking an external login to an existing account requires a verified email and an explicit confirmation step — never an automatic merge based on matching email alone.
- RoundTrip's
TenantUser/Techniciansplit stays structurally incapable of cross-resolving, even after Keystone is in place — a technician and a tenant user sharing an email must never resolve to each other's record via any login path.
Open Notes — needs Pete's confirmation, not yet settled
-
MFA enrollment policy — TOTP confirmed as the method (2026-09-XX). Enrollment trigger still genuinely open: leaning toward tenant-admin-controlled opt-in (a
TenantAdminenables MFA requirement for their own tenant) rather than platform-wide mandatory or fully optional — explicitly flagged by Pete as needing real discussion, not a settled decision. Real architectural dependency this surfaces: Keystone has no tenant concept of its own; a tenant-scoped "require MFA" setting would need to live in Command Center and be communicated to Keystone the same way entitlement claims are, not something Keystone can see directly. -
Audit log retention: confirmed at 1 year (2026-09-XX). Matches PCI-DSS's own 1-year minimum standard — a genuine industry floor, not a compromise. Worth revisiting on a per-customer basis if a future customer's own regulatory obligations (healthcare, finance, government) require longer retention — not a platform-wide change, a possible per-tenant exception.
-
Rate-limiting thresholds — still open, refined. Pete's instinct that Command Center's 10/minute/IP reference point is too lenient for an auth platform is correct, but pure per-IP limiting has a real weakness worth designing around: office networks put many legitimate users behind one shared IP, and a strict IP-only limit risks locking out multiple real employees at once. Proposed direction: a two-layer approach — a moderate per-IP limit (5-8/minute, looser than a pure "high-security" number to tolerate shared-office IPs) combined with a stricter per-account lockout/backoff after repeated failures on that specific account regardless of source IP. Specific numbers still need to be set.
-
Registration policy: confirmed (2026-09-XX). Self-serve for a brand-new tenant's first user; invitation-only for every subsequent user added to an existing tenant — matching RoundTrip's own real, already-built pattern (
ProvisionTenantJobfor the first user,CreateMemberUserAsyncfor team invites) exactly. New open question this surfaces: self-serve tenant registration is really two coordinated operations — a new KeystoneUserand a new Command Center tenant/subscription (including product/tier selection, which is Command Center's data, not Keystone's, per Section 5.5). Whether Keystone's own bare/account/registerpage is replaced by a real, RoundTrip-hosted signup experience that orchestrates both calls is a genuine open design question, not yet settled. -
Session/token revocation: confirmed (2026-09-XX). Both a self-service "log out everywhere" account action and an admin-app action — not either/or.
Document History
| Version | Date | Author | Changes |
|---|---|---|---|
| 1.0 | September 2026 | Pete Carroll | Initial BRD — Keystone's first, written once the OpenIddict spike proved the approach and Command Center's real entitlement endpoint removed the one hard dependency blocking Keystone's "ready to use" tier. Scoped against the full production tier per Pete's explicit 2026-09-06 decision. |