Skip to content

Latest commit

 

History

History
438 lines (328 loc) · 14.4 KB

File metadata and controls

438 lines (328 loc) · 14.4 KB

Project NEXUS - Decision Log & Notes

Current Phase: Phase 11 (Future)


Phase 10 Checklist (COMPLETED)

  • Notification entity with tenant isolation
  • EF Core configuration with global query filter
  • GET /api/notifications (list with pagination)
  • GET /api/notifications/unread-count
  • GET /api/notifications/{id}
  • PUT /api/notifications/{id}/read (mark as read)
  • PUT /api/notifications/read-all (mark all as read)
  • DELETE /api/notifications/{id}
  • Automatic notifications on connection request
  • Automatic notifications on connection accepted
  • Cross-tenant isolation verified
  • All manual tests passing (see PHASE10_EXECUTION.md)

Phase 9 Checklist (COMPLETED)

  • Connection entity with tenant isolation
  • EF Core configuration with global query filter
  • GET /api/connections (list all connections)
  • GET /api/connections/pending (incoming + outgoing)
  • POST /api/connections (send request)
  • PUT /api/connections/{id}/accept (accept request)
  • PUT /api/connections/{id}/decline (decline request)
  • DELETE /api/connections/{id} (remove/cancel)
  • Mutual request auto-accept
  • Cannot connect to yourself validation
  • Cannot duplicate connection validation
  • Cross-tenant isolation verified
  • All manual tests passing (see PHASE9_EXECUTION.md)

Phase 8 Checklist (COMPLETED)

  • RefreshToken entity with tenant isolation
  • PasswordResetToken entity with tenant isolation
  • POST /api/auth/logout (revoke tokens)
  • POST /api/auth/refresh (token rotation)
  • POST /api/auth/register (new user registration)
  • POST /api/auth/forgot-password (request reset)
  • POST /api/auth/reset-password (confirm reset)
  • Refresh token rotation for security
  • Password reset invalidates all sessions
  • Email sending (deferred - requires email service)
  • All manual tests passing (see PHASE8_EXECUTION.md)

Previous Phase: Phase 8 (Authentication Enhancements)

Phase 0: Trust Establishment - COMPLETED ✓

Started: 2026-02-02 Completed: 2026-02-02

Phase 1: Listings READ API - COMPLETED ✓

Started: 2026-02-02 Completed: 2026-02-02

Phase 2: User Profile Update - COMPLETED ✓

Started: 2026-02-02 Completed: 2026-02-02

Phase 3: Listings WRITE - COMPLETED ✓

Started: 2026-02-02 Completed: 2026-02-02

Phase 4: Wallet READ - COMPLETED ✓

Started: 2026-02-02 Completed: 2026-02-02


Decisions

D001: PostgreSQL as Primary Database

Date: 2026-02-02 Status: Decided Context: The legacy PHP system uses MySQL/MariaDB. We need to choose a database for the new backend. Decision: Use PostgreSQL for the new backend. Rationale:

  • Clean break from legacy - no temptation to share databases
  • Better support for advanced features (JSONB, arrays, full-text search)
  • Excellent EF Core support via Npgsql
  • Industry standard for modern .NET applications

Consequences:

  • Two separate databases during transition period
  • Data synchronization will be needed later (out of scope for Phase 0)
  • Cannot reuse PHP database schemas directly

D002: Minimal Architecture for Phase 0

Date: 2026-02-02 Status: Decided Context: The legacy migration documentation includes complex patterns (CQRS, MediatR, Clean Architecture). Decision: Start with the simplest possible architecture that meets Phase 0 objectives. Rationale:

  • Prove core concepts before adding complexity
  • Avoid premature abstraction
  • Easier to debug and understand
  • Can add patterns later when justified by actual needs

What we ARE using:

  • Single Web API project
  • EF Core directly in controllers (for now)
  • Simple services where needed
  • Global query filters for tenant isolation

What we are NOT using (yet):

  • CQRS / MediatR
  • Repository pattern
  • Separate Application/Domain/Infrastructure projects
  • AutoMapper
  • FluentValidation pipeline behaviors

D003: JWT Claim Structure

Date: 2026-02-02 Status: Decided Context: JWTs must be compatible with the legacy PHP system. Decision: Use the following claim structure:

{
  "sub": "12345",           // user_id as string
  "tenant_id": 2,           // integer
  "role": "member",         // string
  "email": "user@example.com",
  "iat": 1706889600,        // issued at (unix timestamp)
  "exp": 1738425600         // expires at (unix timestamp)
}

Notes:

  • sub claim contains user ID (standard JWT practice)
  • tenant_id is custom claim for multi-tenancy
  • Must use same signing key as PHP (HS256)
  • Key stored in configuration, never in code

D004: Tenant Resolution Strategy

Date: 2026-02-02 Status: Decided Context: Need to determine tenant context for every request. Decision: Resolve tenant in the following order:

  1. JWT claim (tenant_id) - for authenticated API requests
  2. X-Tenant-ID header - for service-to-service calls
  3. Reject - if neither present, return 400

Rationale:

  • JWT is the primary source of truth for authenticated users
  • Header fallback allows testing and internal calls
  • No domain-based routing in Phase 0 (complexity not justified yet)
  • Explicit failure is safer than defaulting to a tenant

D005: Login Requires Tenant Identifier

Date: 2026-02-02 Status: Decided Context: User emails are unique per-tenant, not globally. A login with just email+password is ambiguous. Decision: Login endpoint requires tenant_slug or tenant_id in addition to email and password. Rationale:

  • Emails may exist in multiple tenants (e.g., consultant working with multiple orgs)
  • Tenant resolution must happen BEFORE user lookup
  • Mirrors how PHP app handles login (tenant context from domain or explicit selection)

API Contract:

{
  "email": "user@example.com",
  "password": "secret",
  "tenant_slug": "acme"   // OR "tenant_id": 1
}

D006: Tenant Resolution Security Hardening

Date: 2026-02-02 Status: Decided Context: Initial implementation allowed X-Tenant-ID header to set tenant context for any request. Decision: X-Tenant-ID header is ONLY allowed for unauthenticated requests in Development mode.

Rationale:

  • Authenticated requests MUST use tenant_id from JWT - header cannot override
  • Prevents privilege escalation where user could access another tenant's data
  • Development-only header allows testing without valid JWT
  • Production never trusts external headers for tenant context

Security Invariant: For authenticated requests, tenant context comes ONLY from JWT claims.


Assumptions

A001: JWT Secret Availability

Assumption: The JWT signing secret from the PHP system will be provided via configuration. Risk if wrong: JWTs will not be interoperable. Mitigation: Verify with a real token from PHP before Phase 0 completion.


A002: Tenant IDs Are Integers

Assumption: Tenant IDs in the legacy system are integers (not GUIDs or strings). Risk if wrong: Schema mismatch, JWT claim parsing errors. Mitigation: Confirm with legacy database schema.


A003: Single Tenant Per User

Assumption: A user belongs to exactly one tenant at a time. Risk if wrong: Authorization logic would need redesign. Mitigation: Verify with legacy data model.


Open Questions

Q001: Token Expiry Durations

Question: What are the exact token expiry durations used by PHP? Status: Open Impact: Must match for seamless interoperability. Notes: Documentation suggests: Web = 2 hours access, Mobile = 1 year access.


Q002: Refresh Token Strategy

Question: How are refresh tokens stored and validated in PHP? Status: Open Impact: Affects whether we can validate PHP-issued refresh tokens. Notes: May need to defer refresh token support to later phase.


Phase 0 Checklist (COMPLETED)

  • CLAUDE.md created
  • NOTES.md created
  • Project structure created
  • PostgreSQL connection working (Docker on port 5434)
  • JWT generation working
  • JWT validation working (for PHP-issued tokens)
  • Tenant resolution middleware working
  • Global query filter working
  • One entity with tenant isolation (User)
  • Health check endpoint
  • Manual verification tests passed (see PHASE0_EXECUTION.md)

Phase 1 Checklist (COMPLETED)

  • Listing entity created with tenant isolation
  • EF Core configuration with global query filter
  • Listings migration generated
  • ListingsController with GET endpoints
  • Seed data for listings (5 listings across 2 tenants)
  • All manual tests passing (see PHASE1_EXECUTION.md)
  • Cross-tenant isolation verified

Phase 2 Checklist (COMPLETED)

  • PATCH /api/users/me endpoint added
  • Validation for first_name and last_name (max 100 chars, no empty strings)
  • Returns same shape as GET /api/users/me
  • All manual tests passing (see PHASE2_EXECUTION.md)

Phase 3 Checklist (COMPLETED)

  • POST /api/listings endpoint added
  • PUT /api/listings/{id} endpoint added
  • DELETE /api/listings/{id} endpoint added (soft delete)
  • Validation: title required, max 255 chars
  • Validation: type must be "offer" or "request"
  • Owner authorization (only owner can update/delete)
  • Cross-tenant isolation (404 for other tenant's listings)
  • All manual tests passing (see PHASE3_EXECUTION.md)

Phase 4 Checklist (COMPLETED)

  • Transaction entity created with tenant isolation
  • EF Core configuration with global query filter
  • Transactions migration generated
  • WalletController with GET endpoints
  • GET /api/wallet/balance (calculated from transactions)
  • GET /api/wallet/transactions (with pagination and type filter)
  • GET /api/wallet/transactions/{id} (participant check)
  • Seed data for transactions (5 transactions across 2 tenants)
  • Cross-tenant isolation verified
  • All manual tests passing (see PHASE4_EXECUTION.md)

Phase 5 Checklist (COMPLETED)

  • POST /api/wallet/transfer endpoint added
  • Validate: amount > 0
  • Validate: sender != receiver
  • Validate: receiver exists in same tenant
  • Validate: sender has sufficient balance
  • Returns new balance after transfer
  • Transaction stored with status "completed"
  • Cross-tenant isolation verified
  • All manual tests passing (see PHASE5_EXECUTION.md)

Phase 6 Checklist (COMPLETED)

  • Conversation and Message entities created with tenant isolation
  • EF Core configuration with global query filters
  • GET /api/messages (list conversations with last message preview)
  • GET /api/messages/{id} (conversation messages with pagination)
  • GET /api/messages/unread-count (count of unread messages)
  • Participant-only access (non-participants get 404)
  • Seed data for 1 conversation with 5 messages
  • Cross-tenant isolation verified
  • All manual tests passing (see PHASE6_EXECUTION.md)

Phase 7 Checklist (COMPLETED)

  • POST /api/messages endpoint (send message, creates conversation if needed)
  • PUT /api/messages/{id}/read endpoint (mark all messages in conversation as read)
  • Validate: content required, max 5000 characters
  • Validate: cannot message yourself
  • Validate: recipient must exist in same tenant
  • Conversation normalization (smaller participant ID first for uniqueness)
  • Cross-tenant isolation verified
  • All manual tests passing (see PHASE7_EXECUTION.md)

What is Intentionally NOT Implemented (Phase 0)

This section explicitly documents what is out of scope for Phase 0.

Architectural Patterns (Deferred)

Pattern Reason for Exclusion
CQRS Adds complexity without proven need; can add when read/write scaling requirements emerge
MediatR Pipeline behaviors not needed yet; direct service calls are simpler to debug
Repository Pattern EF Core DbContext already provides unit of work and abstraction
Clean Architecture layers Single project is easier to reason about; split when boundaries become clear
AutoMapper Manual mapping is explicit and avoids hidden bugs; can add when DTO count grows
FluentValidation DataAnnotations sufficient for Phase 0; add pipeline behaviors when validation complexity grows

Features (Deferred)

Feature Reason for Exclusion
Refresh tokens ✅ Implemented in Phase 8
2FA / MFA Not required for trust establishment
Rate limiting Add in Phase 1 when auth is stable
Audit logging Add when compliance requirements are clear
Background jobs No async processing needs in Phase 0
Caching (Redis) Optimize when needed, not preemptively
Real-time (SignalR/WebSockets) Feature scope, not infrastructure scope
File uploads Feature scope
Email/SMS notifications Feature scope
Search (Elasticsearch) Feature scope
API versioning Single version for Phase 0

Infrastructure (Deferred)

Component Reason for Exclusion
Docker for API API runs on Kestrel locally; containerize when deployment pipeline is established (PostgreSQL uses Docker)
CI/CD pipelines Set up with first production deployment
Kubernetes/orchestration Premature; single instance is fine for Phase 0
Monitoring (APM) Built-in logging sufficient; add observability when in production
Database migrations in CI Manual migrations acceptable for Phase 0

Integration (Explicitly Out of Scope)

Integration Reason for Exclusion
PHP proxy (Strangler Fig) This backend is independent; no routing from PHP needed yet
Database sharing with PHP Separate databases by design (D001)
Data synchronization Phase 0 is about proving isolation, not sync
Legacy code migration This is a new codebase, not a migration

Testing (Minimal for Phase 0)

Test Type Status
Unit tests Add as code complexity warrants
Integration tests One basic test to prove the stack works
E2E tests Deferred to Phase 1
Load tests Deferred until performance requirements are defined

References