Bumps [postcss](https://github.com/postcss/postcss) from 8.5.19 to 8.5.25. - [Release notes](https://github.com/postcss/postcss/releases) - [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md) - [Commits](https://github.com/postcss/postcss/compare/8.5.19...8.5.25) --- updated-dependencies: - dependency-name: postcss dependency-version: 8.5.25 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|---|---|---|
| .github | ||
| .security-review | ||
| api | ||
| docs/adr | ||
| infra | ||
| lambdas | ||
| mobile | ||
| scripts | ||
| shared/api-contracts | ||
| web | ||
| .gitignore | ||
| AUDIT-REPORT.md | ||
| CLAUDE.md | ||
| docker-compose.yml | ||
| README.md | ||
Proposal System
Internal proposal management platform for Sea Haven Industries. Dispatchers submit proposal requests, AI generates draft line items from historical data via Bedrock RAG, admins review and approve in a pricing workspace, and the system produces branded PDFs for delivery.
Architecture Overview
Monorepo with five primary services:
- .NET 8 API -- Clean Architecture REST API hosted on Lambda behind API Gateway (JWT-authorized) with Function URL (AWS_IAM) for internal access
- React 19 Web -- MUI v9 admin/dispatcher workspace served via CloudFront + S3
- React Native Mobile -- iOS-first field app for dispatchers (offline-capable)
- Python Lambdas -- PDF extraction, PDF generation, library ingestion, AI suggestions, Aurora pgvector bootstrap
- CDK Infrastructure -- Three TypeScript stacks managing all AWS resources
Repository Structure
proposal-system/
├── api/ .NET 8 Web API (Lambda-hosted, EF Core + PostgreSQL)
├── web/ React 19 + MUI v9 + Vite frontend
├── mobile/ React Native 0.86 iOS app
├── lambdas/ Python 3.12 processing functions (arm64)
├── infra/ CDK TypeScript (3 stacks)
├── shared/ TypeScript API contracts (shared between web + mobile)
├── scripts/ Post-deploy and utility scripts
├── .github/ CI/CD workflows
└── docker-compose.yml
Tech Stack
| Component | Technologies |
|---|---|
| API | .NET 8, ASP.NET Core, EF Core + Npgsql, FluentValidation, Cognito JWT, Amazon.Lambda.AspNetCoreServer |
| Web | React 19, TypeScript, MUI v9, Vite, TanStack Query, react-hook-form + zod, axios |
| Mobile | React Native CLI 0.86, React 19, React Native Paper, React Navigation, react-native-app-auth (PKCE), amazon-cognito-identity-js (SRP), Keychain, offline draft queue |
| Lambdas | Python 3.12, arm64, pdfplumber, reportlab, httpx, boto3 |
| Infrastructure | CDK TypeScript (aws-cdk-lib pinned exact, kept current by Dependabot) |
| AI/RAG | Bedrock Knowledge Base (Titan Embeddings v2), Aurora PostgreSQL + pgvector vector store (see ADR 0001), Claude Sonnet via Bedrock cross-region inference |
| Auth | Cognito User Pool + Google OAuth IdP (groups: dispatchers, admins, sysadmins) |
AWS Resources
All resources deploy to us-east-1 in the seahaven-prod workload account (011934824531).
The mgmt account (328440206208) is frozen for new workloads. Staging is not currently
deployable — it still references the frozen mgmt account and is hard-blocked in infra/lib/config.ts
until retargeted to seahaven-dev (710827005802). Prod deploys go through the deploy.yaml
pipeline (workflow_dispatch) only; no manual/local cdk deploy to prod.
| CDK Stack | Key Resources |
|---|---|
proposal-system-foundation |
Aurora PostgreSQL 15 Serverless v2 (RDS Data API, pgvector), S3 buckets, SQS queue + DLQ, Cognito user pool, Secrets Manager |
proposal-system-compute |
API Gateway HTTP API (JWT authorizer + access logging), .NET 8 API Lambda + Function URL (AWS_IAM), Python Lambdas (pdf-extract, pdf-generate, library-ingest, suggestions, aurora-pgvector-init bootstrap), Bedrock KB (Aurora pgvector store) |
proposal-system-frontend |
CloudFront distribution (S3 OAC) |
| Resource Type | Names |
|---|---|
| S3 Buckets | proposal-system-uploads, proposal-system-generated, proposal-system-library, seahaven-ios-certificates |
| SQS | proposal-system-jobs (720s visibility, SQS-managed encryption, reportBatchItemFailures) + proposal-system-jobs-dlq (SQS-managed encryption, message body filtering by jobType) |
| Secrets | proposal-system/db-credentials, proposal-system/internal-api-key |
Local Development
Prerequisites
- .NET 8 SDK
- Node.js 24+
- Python 3.12
- PostgreSQL 16 (via docker-compose or native)
Database
docker compose up -d # starts PostgreSQL on port 5432
# database: proposalsystem, password: localdev
API
cd api
dotnet restore
dotnet run --project src/ProposalSystem.Api
# runs on http://localhost:5000
In development mode (DevMode=true in appsettings.Development.json):
- JWT auth uses a local symmetric HMAC key (no Cognito required)
- S3 service returns fake presigned URLs
- SQS publisher logs messages without sending
Web Frontend
cd web
npm install
npm run dev
# runs on http://localhost:5173, proxies /api to localhost:5000
When VITE_COGNITO_CLIENT_ID is not set, the login screen shows role-selector buttons for local development.
Infrastructure
cd infra
npm install
npx cdk synth
CI/CD
CI (on pull request to main)
Six parallel jobs calling org reusable workflows:
| Job | Workflow | What it checks |
|---|---|---|
| .NET Build & Test | ci-dotnet.yaml |
Restore, build, test the API solution (xUnit) |
| Web Frontend Check | ci-typescript-frontend.yaml |
Prettier format:check, build (includes tsc -b), vitest suite, Playwright chromium smoke (dev-login → proposal list, API mocked) |
| Mobile Typecheck | ci-typescript-cdk.yaml |
TypeScript typecheck for mobile |
| Python Lint | ci-python-sam.yaml |
ruff check + format on lambdas/ |
| Python Tests | inline job | pytest suite (37 tests — pdf-generate, suggestions, library-ingest, internal API signing) |
| CDK Synth | ci-typescript-cdk.yaml |
Synthesize CDK stacks (includes .NET publish) |
Deploy (manual)
Auto-deploy on push to main is currently disabled (PR #167) — both deploy workflows run via workflow_dispatch from the Actions tab. deploy.yaml calls the cd-cdk.yaml reusable workflow:
- Publishes .NET 8 API and Python Lambdas
- Runs
cdk deploy --all - Executes
scripts/post-deploy.sh(builds web, syncs to S3, invalidates CloudFront)
Deploy uses OIDC role githubdeploy-proposal-system. Concurrency group prevents parallel deploys.
Mobile Deploy
Workflow: deploy-mobile.yaml -- builds and uploads to TestFlight via cd-mobile-ios.yaml reusable workflow on macos-26.
Trigger: manual only (workflow_dispatch) while the app is pre-V1, to save macOS runner cost.
Mobile iOS
The iOS app uses Fastlane with match for code signing. Certificates and profiles are stored in the seahaven-ios-certificates S3 bucket (versioning enabled, public access blocked).
Build and upload to TestFlight is handled by the cd-mobile-ios.yaml reusable workflow. Required secrets:
| Secret | Purpose |
|---|---|
AWS_DEPLOY_ROLE_ARN |
OIDC role for match S3 access |
MATCH_PASSWORD |
Decryption passphrase for signing assets |
ASC_KEY_ID |
App Store Connect API key ID |
ASC_ISSUER_ID |
App Store Connect issuer |
ASC_KEY_CONTENT |
App Store Connect API key (base64) |
Authentication & Authorization
Two-layer auth architecture with defense-in-depth:
| Path | Authorizer | Authentication |
|---|---|---|
External clients → API Gateway /{proxy+} |
Cognito JWT authorizer (web + mobile client IDs) | .NET JWT middleware (ValidateAudience=true) |
/api/health |
None (public) | None |
/api/auth/callback, /api/auth/dev-login |
None (unauthenticated) | None (pre-auth endpoints) |
| Internal Lambdas → Function URL | AWS_IAM (grantInvokeUrl) | Internal API key (X-Internal-Api-Key header, value from Secrets Manager) |
Role-based access: Cognito groups (dispatchers, admins, sysadmins) map to API roles via cognito:groups claim. Dispatchers can only see their own proposals (ownership enforced in service layer). VendorProposals and GeneratedPdfs endpoints restricted to admins/sysadmins.
Internal API key: Python Lambdas call the .NET API via a Lambda Function URL with AWS_IAM auth (bypasses API Gateway JWT check). The InternalApiKeyMiddleware validates the X-Internal-Api-Key header and assigns the admins role to the synthetic identity. Lambdas cache the API key from Secrets Manager with a 5-minute TTL.
Optimistic concurrency (ADR 0004): proposal responses carry an opaque rowVersion token; mutations of the proposal aggregate (update, approve, return-to-review, send, revise, bulk line-item update) require proposalVersion in the body. Missing token → 422 ProposalVersionRequired, malformed → 422 InvalidRowVersion, stale → 409 { message, currentState } with the reloaded proposal embedded (unguarded races → 409 { status, message, code }). Line-item create/delete are token-less but bump the aggregate version. Audit rows commit atomically with their mutation (stage-then-single-SaveChanges).
Deploy note for schema changes: EF migrations auto-apply at API startup under a pg_advisory_lock. Before deploying a migration: take a manual RDS snapshot; additive-only migrations are backward-compatible with the previous Lambda version. Test the down-script against a snapshot-restored copy before any production rollback.
Data Flow
- Dispatcher submits proposal request (web or mobile)
- API creates proposal record (with advisory-locked number generation), publishes SQS message
- If vendor PDF attached:
pdf-extractLambda parses and structures data - Suggestions Lambda queries Bedrock KB for similar proposals, generates line items via Claude
- Admin reviews/edits line items in pricing workspace
- On approval:
pdf-generateLambda creates branded PDF - On send:
library-ingestLambda adds approved proposal to KB for future matching
Failed SQS messages are reported via batchItemFailures and retried up to 3 times before moving to the DLQ.
Testing
186 tests across three stacks, all run in CI on every PR:
| Suite | Framework | Count | Coverage |
|---|---|---|---|
| .NET API | xUnit | 123 | State machine transitions, authorization attributes, middleware, validators, ProposalNumberGenerator, LineItemService state guards |
| Web | vitest | 26 | ProtectedRoute, RoleGuard, API client interceptor (401 logout, token attachment) |
| Python Lambdas | pytest | 37 | pdf-generate, suggestions, library-ingest handler contracts, internal API signing |
cd api && dotnet test # .NET tests
cd web && npm test # vitest
cd lambdas && python -m pytest # pytest
Security
Hardening applied across all layers (see AUDIT-REPORT.md for full details):
- Auth: Cognito JWT validation with audience check, startup fails if auth not configured, DevMode gated to
IsDevelopment() - API: FluentValidation on all DTOs, generic error responses (no stack traces or config leaks), structured audit logging with before/after diffs
- Function URL: AWS_IAM auth + internal API key (two-layer defense)
- Infrastructure: S3
enforceSSL+BLOCK_ALL, SQS managed encryption, Aurora in private subnets, Cognito optional TOTP MFA, API Gateway access logging - Lambdas: Prompt injection sanitization, PDF size limits, numeric validation on AI suggestions, S3 key sanitization, idempotent SQS processing
- CI/CD: OIDC (no long-lived credentials), org reusable workflows (
@main),--require-approval broadeningon local deploys - Web: sessionStorage for tokens (not localStorage), error boundaries, role guards on all admin routes, 401 interceptor clears auth state