Tickets Document
Here is your step-by-step ticket backlog to build MockBoard. Whether you're pasting these sequentially into Claude Code or importing them into Linear, Jira, or ClickUp for your team, these tickets provide the exact implementation steps to bring this architecture to life.
Phase 1: Infrastructure Foundation
Ticket 1.1: Create Network Foundation Stack
Objective: Provision the foundational AWS networking infrastructure, including the VPC, subnets across multiple availability zones, security groups, and VPC endpoints.
Context: This ticket establishes the secure and isolated network environment required by all other services, as specified in the Architecture Design Document. Creating the NetworkStack first allows subsequent data and compute resources to be placed into the correct, pre-configured private subnets with appropriate firewall rules.
Technical Directives:
- Implement a CDK stack named
NetworkStack in infra/lib/network-stack.ts.
- Create a new VPC spanning two Availability Zones.
- Configure three subnet tiers within the VPC:
public, private-app, and private-data.
- Create a gateway VPC endpoint for S3.
- Create interface VPC endpoints for Bedrock, Secrets Manager, and ECR.
- Define security groups for the ALB, FastAPI service, orchestration/ingestion workers, RDS, and Redis.
- Configure security group ingress and egress rules precisely as defined in the phase contract, allowing traffic only from specified sources (e.g., RDS only accepts traffic from the application and worker security groups).
- The stack must export its created VPC, subnet, and security group resources for consumption by other stacks.
Scope Boundaries:
- AWS CDK:
infra/lib/network-stack.ts
Acceptance Criteria:
- The
NetworkStack CDK stack deploys successfully.
- A VPC with public, private-app, and private-data subnets exists in the AWS account.
- All specified security groups are created with the correct ingress/egress rules.
- All specified VPC endpoints are created and associated with the correct VPC route tables.
- The stack's outputs (VPC, subnets, security groups) are available for import by other CDK stacks.
Dependencies:
None
Ticket 1.2: Create Data and Storage Stack
Objective: Provision all stateful data stores, including the PostgreSQL database, Redis cache, S3 buckets, SQS queues, and associated monitoring.
Context: This ticket creates the persistence and messaging layer defined in the DataStack contract, using the network foundation from the previous ticket. The Architecture Design Document specifies using managed AWS services (RDS, ElastiCache, S3, SQS) to minimize operational overhead for the team.
Technical Directives:
- Implement a CDK stack named
DataStack in infra/lib/data-stack.ts.
- Provision an RDS PostgreSQL 16 instance (db.r7g.large) in the
private-data subnets, configured for Multi-AZ.
- Enable the
pgvector extension on the RDS instance.
- Configure RDS storage as 500 GB gp3 with auto-scaling up to 2 TB.
- Provision an ElastiCache for Redis 7.x cluster (cache.r7g.large) in the
private-data subnets, configured for Multi-AZ with automatic failover and cluster mode disabled.
- Retrieve the Redis authentication token from AWS Secrets Manager.
- Create two S3 buckets: one for uploads and one for reports, both with versioning, server-side KMS encryption, and Block Public Access enabled.
- Configure an S3 event notification on the uploads bucket to send messages to an SQS ingestion queue.
- Create three SQS queues:
ingestion, ingestion-dlq, and completion. The ingestion queue must be configured to move messages to the DLQ after 4 failed receives and retain messages for 24 hours.
- Create a CloudWatch alarm that triggers an SNS topic if the RDS instance's write IOPS exceeds 3,000 for a sustained 5-minute period.
- The stack must export the names, ARNs, and endpoints of its created resources.
Scope Boundaries:
- AWS CDK:
infra/lib/data-stack.ts
Acceptance Criteria:
- The
DataStack CDK stack deploys successfully.
- The RDS instance is running, accessible from the
private-app subnets, and has the pgvector extension available.
- The ElastiCache for Redis cluster is running and accessible from the
private-app subnets.
- Both S3 buckets and all three SQS queues are created with the specified configurations.
- The CloudWatch alarm for RDS write IOPS is created and configured.
- The created RDS and Redis instances are ready to be configured with the schema and data models defined in the Following Phase.
Dependencies:
Ticket 1
Ticket 1.3: Create Compute and Container Stack
Objective: Provision the AWS ECS Fargate compute infrastructure, including the cluster, ECR repositories, Application Load Balancer, and task definitions for all backend services.
Context: This ticket defines the runtime environments for the application's containerized services, as outlined in the Architecture Design Document. It implements the core "one task per session" strategy for orchestration workers and sets up the auto-scaling control plane service.
Technical Directives:
- Implement a CDK stack named
ComputeStack in infra/lib/compute-stack.ts.
- Create an ECS cluster with Container Insights enabled.
- Create three ECR repositories:
fastapi-control-plane, orchestration-worker, and ingestion-worker.
- Provision an internet-facing Application Load Balancer in the public subnets.
- Define an ECS task definition for the FastAPI control plane (1 vCPU, 2GB RAM) with an IAM role granting permissions for SQS, ECS RunTask, Secrets Manager, SES, S3, and Bedrock.
- Define an ECS task definition for the orchestration worker (2 vCPU, 4GB RAM) with an IAM role granting permissions for RDS, ElastiCache, API Gateway Management API, S3, Bedrock, and Secrets Manager.
- Define an ECS task definition for the ingestion worker (1 vCPU, 2GB RAM) with an IAM role granting permissions for SQS, S3, Bedrock, and Secrets Manager.
- Configure an ECS Service for the FastAPI control plane with a minimum of 2 and a maximum of 20 tasks, scaling based on an ALB request count target of 1,000 RPM.
- Configure an ECS Service for the ingestion worker with a minimum of 0 and a maximum of 10 tasks, scaling based on the depth of the SQS ingestion queue.
- Attach an AWS WAF WebACL to the ALB with rules for rate limiting, SQL injection, and request size constraints.
- Configure the orchestration worker task definition to use a Fargate Spot capacity provider with an on-demand fallback.
- The stack must export the ARNs and names of its created resources.
Scope Boundaries:
- AWS CDK:
infra/lib/compute-stack.ts
Acceptance Criteria:
- The
ComputeStack CDK stack deploys successfully.
- The ECS cluster and all three ECR repositories are created.
- The ALB is created and correctly forwards traffic to the FastAPI service's target group.
- All three ECS task definitions and the two ECS services are created with their specified configurations and IAM roles.
- The ECR repositories are ready to receive Docker images from the CI/CD pipeline.
- The task definitions are ready to be used by the application code deployments in subsequent phases.
Dependencies:
Ticket 1, Ticket 2
Ticket 1.4: Create Application Integration Stack
Objective: Provision the application-facing services, including API Gateways, Lambda authorizers and handlers, the Cognito User Pool, and the Amplify frontend application.
Context: This ticket creates the primary user entry points and authentication layer as defined in the AppStack contract. It integrates serverless components like API Gateway and Lambda with the stateful data and compute backends to complete the infrastructure setup.
Technical Directives:
- Implement a CDK stack named
AppStack in infra/lib/app-stack.ts.
- Create an API Gateway REST API (HTTP API v2) with a default proxy route to the ALB created in the
ComputeStack.
- Create an API Gateway WebSocket API with
$connect, $disconnect, and $default routes.
- Create a Cognito User Pool and App Client configured for email/password sign-in and TOTP MFA support.
- Configure a Cognito JWT authorizer on the REST API and a Lambda authorizer on the WebSocket
$connect route.
- Implement the
$connect Lambda to read a session_id from the query string and store the connection ID in the Redis set ws:connections:{session_id}. The Lambda must also write the Management API endpoint URL to the Redis key ws:endpoint.
- Implement the
$disconnect Lambda to remove the connection ID from the corresponding Redis set.
- Implement a Cognito post-confirmation Lambda trigger that will create a new user record in RDS. (Note: The database connection logic will be a placeholder until Phase 2).
- Create an AWS Amplify application configured to point to the project's GitHub repository.
- Configure an SES email identity for sending transactional emails.
- Enable AWS X-Ray tracing on the FastAPI task definition and the API Gateways.
- Create CloudWatch alarms for FastAPI task count, worker failure rate, DLQ depth, API Gateway 5xx rate, ElastiCache node failure, and RDS failover, all reporting to an SNS topic.
Scope Boundaries:
- AWS CDK:
infra/lib/app-stack.ts
Acceptance Criteria:
- The
AppStack CDK stack deploys successfully.
- The Cognito User Pool is created and allows user sign-up and sign-in.
- The REST API routes requests to the ALB, and the WebSocket API manages connections.
- Connecting to the WebSocket API successfully triggers the
$connect Lambda, which writes the connection ID to the correct key in Redis.
- Disconnecting from the WebSocket API triggers the
$disconnect Lambda, which removes the connection ID from Redis.
- All specified CloudWatch alarms are created and configured.
Dependencies:
Ticket 1, Ticket 2, Ticket 3
Ticket 1.5: Implement CI/CD Pipeline Workflows
Objective: Create the GitHub Actions workflow files to automate infrastructure and application linting, testing, and deployment.
Context: This ticket establishes the automated CI/CD pipeline specified in the Deployment Architecture. This pipeline is crucial for ensuring code quality and enabling repeatable, safe deployments to both staging and production environments, with a manual gate for production releases.
Technical Directives:
- Create a GitHub Actions workflow file at
.github/workflows/pr.yml.
- Configure the
pr.yml workflow to trigger on every pull request to the main branch.
- The
pr.yml workflow must execute linting, type-checking, and a cdk diff command to preview infrastructure changes.
- Create a GitHub Actions workflow file at
.github/workflows/deploy.yml.
- Configure the
deploy.yml workflow to trigger on every merge to the main branch.
- The
deploy.yml workflow must build placeholder Docker images for all services, push them to the ECR repositories tagged with the Git SHA, and deploy all CDK stacks to the staging environment.
- After the staging deployment, the workflow must include a placeholder for integration tests.
- After the tests, the workflow must use a GitHub Actions environment to require manual approval before proceeding.
- Upon approval, the workflow must deploy all CDK stacks to the
production environment.
Scope Boundaries:
- GitHub Actions configuration files:
.github/workflows/
Acceptance Criteria:
- Creating a pull request against the
main branch successfully triggers the pr.yml workflow, and the cdk diff step completes.
- Merging a pull request to the
main branch successfully triggers the deploy.yml workflow.
- The
deploy.yml workflow successfully pushes placeholder images to all ECR repositories.
- The
deploy.yml workflow successfully completes the cdk deploy --all step for the staging environment.
- The
deploy.yml workflow correctly pauses and waits for manual approval before the production deployment step.
Dependencies:
Ticket 1, Ticket 2, Ticket 3, Ticket 4
Phase 2: Database Schema and Shared Data Layer
Ticket 2.1: Create Database Schema with Alembic Migration
Objective: This ticket creates the initial Alembic migration file that defines all required database tables, columns, constraints, and indexes for the entire application.
Context: This foundational schema supports all core entities described in the Architecture Design Document, including multi-tenancy, session management, message history, and the dual-layer RAG storage (ethnographic_chunks, product_context_chunks). It also includes the langgraph_thread_tenant_map table required for the GDPR erasure workflow to correctly identify and purge LangGraph checkpoint data from RDS.
Technical Directives:
- Create a new Alembic migration script.
- The script must define all tables and columns exactly as specified in the
Phase 2: Database Schema contract, including tenants, workspaces, users, workspace_members, sessions, persona_configs, messages, analyst_reports, ethnographic_chunks, product_context_chunks, gdpr_audit_log, and langgraph_thread_tenant_map.
- All primary keys, foreign keys, data types (including ENUMs and
vector(1536)), NOT NULL constraints, and default values must match the contract.
- The script must create HNSW indexes on the
embedding columns of ethnographic_chunks and product_context_chunks using cosine distance.
- The script must create all other specified b-tree indexes on foreign keys and commonly queried columns.
- The migration must not create the
AsyncPostgresSaver checkpoint tables; these are managed by the LangGraph library at runtime.
Scope Boundaries:
- Database migration script (
backend/migrations/versions/)
Acceptance Criteria:
- The Alembic migration runs successfully from a clean state against the RDS PostgreSQL instance.
- All specified tables, columns, constraints, and indexes are present in the database after the migration completes.
- The data types for all columns, including ENUMs and vector dimensions, match the specification.
- The HNSW indexes are configured for cosine similarity search.
Dependencies: None
Ticket 2.2: Implement SQLAlchemy ORM Models
Objective: This ticket creates the SQLAlchemy ORM model classes that map directly to the database schema established in the previous ticket.
Context: These ORM models provide a type-safe, object-oriented interface to the database for all backend services (FastAPI control plane, orchestration worker, ingestion worker). As per the Architecture Design Document, every model must support tenant isolation by including a tenant_id attribute, which is fundamental to the system's security architecture.
Technical Directives:
- Create SQLAlchemy declarative model classes for every table defined in the
001_initial_schema.py migration.
- The class names must be
Tenant, Workspace, User, WorkspaceMember, Session, PersonaConfig, Message, AnalystReport, EthnographicChunk, ProductContextChunk, GdprAuditLog, and LangGraphThreadTenantMap.
- All model attributes must correctly map to their corresponding database columns, including data types and relationships.
- All models intended for tenant-scoped data must include the
tenant_id attribute.
Scope Boundaries:
- Shared ORM models module (
backend/shared/models.py)
Acceptance Criteria:
- The
backend/shared/models.py module can be imported without errors.
- Instantiating a model object and accessing its attributes does not raise an error.
- When used with a SQLAlchemy session, the models can be used to successfully insert and retrieve data from their corresponding tables.
Dependencies: Ticket 1
Ticket 2.3: Define Pydantic API and Message Schemas
Objective: This ticket defines the Pydantic models that serve as the data transfer objects (DTOs) for API requests, responses, and internal WebSocket messages.
Context: These schemas establish the formal data contracts between the frontend client, the API layer, and the orchestration workers, as outlined in the Architecture Design Document. Defining these upfront ensures consistency and provides a clear handoff to the Following Phase (Phase 3), which will build the API endpoints that consume and produce these shapes.
Technical Directives:
- Implement all Pydantic V2 models as specified in the
Phase 2: Contracts section.
- Create the
SessionStatus and AgentRole string enums.
- Implement the
PersonaConfigCreate, SessionCreate, MessageOut, SessionOut, and WorkspaceMessagePush models with the exact field names and types specified.
- Use Python's
UUID and datetime types where appropriate.
- Ensure the
personas list in SessionCreate is correctly typed as list[PersonaConfigCreate].
- The
WorkspaceMessagePush schema must accommodate different event types and optional payloads as defined in the contract.
Scope Boundaries:
- Shared Pydantic schemas module (
backend/shared/schemas.py)
Acceptance Criteria:
- The
backend/shared/schemas.py module can be imported without errors.
- Pydantic models can be instantiated with valid data and raise validation errors for invalid data (e.g., wrong types, missing required fields).
- The
SessionCreate model correctly validates that the length of the personas list matches the persona_count value in consuming code.
- The
WorkspaceMessagePush schema can be used to serialize a payload for the agent_message event, which will be used by the orchestration worker in a later phase.
Dependencies: None
Ticket 2.4: Create Shared Database Connection Module
Objective: This ticket creates a reusable Python module for managing the asynchronous database connection pool and sessions.
Context: This module centralizes database connection logic, ensuring that all backend services connect to RDS PostgreSQL consistently. It provides the foundation for application-level Row-Level Security by including the function to set the tenant context on a per-session basis, a key security control from the Architecture Design Document.
Technical Directives:
- Implement the
backend/shared/db.py module with the specified asynchronous functions: init_db, get_db_session, set_tenant_context, and close_db.
init_db must create a module-level async SQLAlchemy engine and sessionmaker from a provided database URL and register the pgvector extension.
get_db_session must be an async generator that yields a new AsyncSession.
set_tenant_context must execute the SET LOCAL app.current_tenant_id command on a given session.
close_db must properly dispose of the engine resources.
Scope Boundaries:
- Shared database connection module (
backend/shared/db.py)
Acceptance Criteria:
- Calling
init_db successfully establishes a connection pool to the RDS instance.
get_db_session yields a functional AsyncSession object that can be used to execute queries.
set_tenant_context successfully sets the session variable in PostgreSQL, verifiable by executing SHOW app.current_tenant_id within the same transaction.
- Calling
close_db terminates the connection pool gracefully.
Dependencies: Ticket 1
Ticket 2.5: Implement Row-Level Security Policies and Tenant Context Management
Objective: This ticket applies Row-Level Security (RLS) policies to all tenant-scoped tables in the database to enforce data isolation at the database layer.
Context: RLS is a defense-in-depth security measure mandated by the Architecture Design Document to prevent cross-tenant data leakage, even in the event of an application-layer bug. This ticket implements the PostgreSQL RLS policies and integrates them with the application's tenant context management.
Technical Directives:
- Augment the Alembic migration from Ticket 1 to enable Row-Level Security on the specified tables:
sessions, messages, persona_configs, analyst_reports, product_context_chunks, workspace_members, workspaces, and users.
- For each table, create a policy that permits access only when the row's
tenant_id column matches the value of the app.current_tenant_id session setting.
- The
set_tenant_context function in backend/shared/db.py will be used by the application layer in a later phase to set this session variable for each request.
Scope Boundaries:
- Database migration script (
backend/migrations/versions/)
- Shared database connection module (
backend/shared/db.py)
Acceptance Criteria:
- After running the migration, RLS is enabled on all specified tables.
- A test connecting to the database confirms that a
SELECT query on a protected table returns no rows if app.current_tenant_id is not set.
- After setting
app.current_tenant_id to Tenant A's UUID, a SELECT query returns only rows belonging to Tenant A and excludes rows from Tenant B.
- The
set_tenant_context function correctly sets the required session variable for the RLS policies to function.
Dependencies: Ticket 1, Ticket 4
Ticket 2.6: Create Shared Redis Client Module
Objective: This ticket creates a reusable Python module for managing the asynchronous connection to the ElastiCache Redis cluster.
Context: This module provides a centralized client for all Redis interactions, as specified in the Architecture Design Document. It will be used by the API layer to manage WebSocket connection IDs and by the orchestration worker for session heartbeating and retrieving connection IDs for pushing messages.
Technical Directives:
- Implement the
backend/shared/redis_client.py module with the specified asynchronous functions: init_redis, get_redis, and close_redis.
init_redis must create a module-level aioredis connection pool from a provided Redis URL.
get_redis must return a Redis client instance from the pool.
close_redis must properly close the connection pool.
- The module must define the exact string constants for Redis keys as specified in the contract:
WS_CONNECTIONS_KEY, HEARTBEAT_KEY, and MANAGEMENT_ENDPOINT_KEY.
Scope Boundaries:
- Shared Redis client module (
backend/shared/redis_client.py)
Acceptance Criteria:
- Calling
init_redis successfully connects to the ElastiCache Redis instance.
- The client returned by
get_redis can successfully execute Redis commands (e.g., SET, GET, SADD).
- The defined key constants (
WS_CONNECTIONS_KEY, etc.) are importable and correctly formatted for use with string substitution.
- Calling
close_redis closes the connection pool without errors.
Dependencies: None
Ticket 2.7: Define Shared Custom Error Classes
Objective: This ticket establishes a standardized set of custom exception classes for handling application-specific errors consistently across all backend services.
Context: As per the Error Handling Policy in the Architecture Design Document, the system must return generic error messages to clients while logging detailed internal information. These custom exceptions provide the mechanism for services to signal specific failure modes (e.g., authorization failure, invalid state transition) which can then be caught by a global exception handler in the API layer.
Technical Directives:
- Create the
backend/shared/errors.py module.
- Define the base
AppError class which accepts a message and a correlation ID.
- Define the specific exception classes that inherit from
AppError: AuthorizationError, ValidationError, SessionStateError, IngestionError, CheckpointError, LLMError, and CapacityError.
Scope Boundaries:
- Shared errors module (
backend/shared/errors.py)
Acceptance Criteria:
- The
backend/shared/errors.py module can be imported without errors.
- Each custom exception class can be raised and caught correctly.
- An instance of
AppError or its subclasses correctly stores the message and correlation ID passed to its constructor.
Dependencies: None
Ticket 2.8: Implement Shared Text Sanitization Utility
Objective: This ticket creates a reusable function for sanitizing user-provided text inputs to mitigate security risks like prompt injection.
Context: The Architecture Design Document's Input Validation section requires that all user-supplied text be sanitized before being used in LLM prompts or stored in the database. This centralized utility ensures that sanitization is applied consistently by all services that process user input.
Technical Directives:
- Create the
backend/shared/sanitize.py module.
- Implement the function
sanitize_user_text which accepts a string and a maximum length integer.
- The function must remove Unicode control characters in the
Cc and Cf categories, while preserving essential whitespace like tabs and newlines.
- The function must truncate the input string to the specified maximum length.
Scope Boundaries:
- Shared sanitization module (
backend/shared/sanitize.py)
Acceptance Criteria:
- The
sanitize_user_text function returns a string with control characters (e.g., \x00, \x08) removed.
- The function correctly truncates a string that is longer than the
max_length argument.
- The function preserves standard characters, numbers, punctuation, and essential whitespace.
- A string shorter than
max_length with no control characters is returned unchanged.
Dependencies: None
Phase 3: Authentication and Tenant Management
Ticket 3.1: FastAPI Application Entry Point and Core Middleware
Objective: Establish the main FastAPI application shell with essential middleware for security, error handling, and lifecycle management.
Context: This ticket creates the foundational structure of the control_plane service as described in the Architecture Design Document. It implements the application entry point, including startup and shutdown events, global exception handling, and security headers, which are required before any API routes can be added.
Technical Directives:
- Implement a FastAPI application in
backend/control_plane/main.py.
- Create a lifespan context manager that calls
init_db() and init_redis() from the shared modules on startup, and close_db() and close_redis() on shutdown.
- Configure CORS middleware to allow only the origin specified in an environment variable.
- Implement middleware to add the following security headers to all responses:
Strict-Transport-Security, X-Content-Type-Options, X-Frame-Options, and Content-Security-Policy.
- Implement a global exception handler that catches all
AppError subclasses defined in backend/shared/errors.py.
- The exception handler must log the full error details and return a generic JSON error response to the client containing a correlation ID, without exposing internal details like stack traces.
- Enable AWS X-Ray middleware for distributed tracing.
Scope Boundaries:
- FastAPI application entry point (
main.py)
- Core application middleware configuration
Acceptance Criteria:
- The application starts without errors, successfully initializing database and Redis connections.
- A request from an allowed origin receives a response with the correct CORS headers.
- A request from a disallowed origin is blocked by CORS policy.
- All API responses include the specified security headers (
HSTS, X-Content-Type-Options, etc.).
- When a route handler raises a custom
AppError, the client receives a JSON response with a generic message and a correlation ID, and the full error is logged.
Dependencies: None
Ticket 3.2: Cognito JWT Verification Middleware
Objective: Implement middleware to validate Cognito JWTs and extract user claims for authenticated requests.
Context: This ticket provides the core authentication mechanism for the entire API, as outlined in the Architecture Design Document. It creates a reusable FastAPI dependency that all protected endpoints will use to ensure requests are from authenticated users and to establish tenant context for database queries.
Technical Directives:
- In
backend/control_plane/auth/middleware.py, create the verify_cognito_token function as specified in the phase contract.
- The function must fetch the JSON Web Key Set (JWKS) from the Cognito User Pool endpoint.
- The JWKS response must be cached in memory with a time-to-live (TTL) to avoid excessive requests.
- The function must validate the JWT's signature, expiry, issuer, and audience against the configured Cognito User Pool details.
- On successful validation, the function must return a
TokenClaims object populated with claims from the token.
- If validation fails for any reason, the function must raise an
AuthorizationError.
- Create a FastAPI dependency (
get_current_user) that injects the get_db_session dependency, retrieves and validates the token via verify_cognito_token, and executes set_tenant_context(db, tenant_id) on the yielded AsyncSession before returning the user claims.
Scope Boundaries:
- Authentication middleware module
Acceptance Criteria:
- A request to an endpoint using the dependency with a valid JWT succeeds.
- A request with an expired JWT is rejected with an
AuthorizationError.
- A request with an invalid signature is rejected with an
AuthorizationError.
- A request with a missing
Authorization header is rejected with an AuthorizationError.
- On a successful authenticated request, the
app.current_tenant_id session variable is correctly set in the database connection before the route handler executes.
Dependencies: 1
Ticket 3.3: Cognito Post-Confirmation Lambda Function
Objective: Create a Lambda function to provision a new user, tenant, and workspace in the database when a user confirms their account in Cognito.
Context: This function automates the user onboarding process, linking the Cognito identity to the application's internal data model as described in the ADD. It ensures that every new user who signs up is correctly provisioned with their own tenant and default workspace, enabling the bootstrap flow.
Technical Directives:
- Create a Lambda function defined in
backend/lambdas/cognito_post_confirmation/handler.py.
- The handler must parse the user's
sub, email, and custom attributes from the Cognito PostConfirmation trigger event.
- If the
custom:tenant_id attribute is absent, the function must create a new Tenant record in the database, using the custom:gdpr_region attribute.
- The function must create a
User record in the database, linking the Cognito sub to the newly created or existing tenant ID.
- The function must create a default
Workspace for the new tenant.
- The function must create a
WorkspaceMember record, making the new user an owner of the default workspace.
- The function must use a direct
psycopg2 connection, not the shared SQLAlchemy engine, as specified in the contract.
- The handler must return the event object unchanged to satisfy Cognito's trigger requirements.
- The Lambda function must be explicitly configured (via CDK in the AppStack) to run inside the VPC's private-app subnets and attached to a security group that allows outbound traffic to the RDS instance's security group, otherwise it will silently time out connecting to the database.
Scope Boundaries:
- Cognito Lambda function module
Acceptance Criteria:
- When a new user confirms their email in Cognito, the Lambda is triggered.
- A
Tenant, User, Workspace, and WorkspaceMember record are successfully created in the RDS database.
- The
User.id in the database matches the Cognito user's sub.
- The
Tenant.id is correctly associated with the new User.
- The Lambda function completes successfully and does not cause the Cognito confirmation flow to fail.
Dependencies: None
Ticket 3.4: Tenant Management API Router
Objective: Implement the API endpoints for creating and retrieving tenant information.
Context: This ticket builds the first set of business logic endpoints, allowing new users to bootstrap their accounts and retrieve their tenant details. This is a foundational part of the multi-tenant architecture and depends on the JWT middleware to identify the user.
Technical Directives:
- Create the tenant router in
backend/control_plane/routers/tenants.py.
- Implement the
POST /tenants endpoint, which is only callable by a user who does not yet have a tenant_id claim.
- The
POST /tenants endpoint must create a Tenant, a default Workspace, and a WorkspaceMember record making the caller the owner. It should return a TenantOut schema.
- Implement the
GET /tenants/me endpoint.
- This endpoint must use the
get_current_user dependency to get the authenticated user's claims.
- It must query and return the
Tenant record corresponding to the tenant_id from the user's JWT claims.
Scope Boundaries:
- FastAPI router module (
tenants.py)
Acceptance Criteria:
- A POST request to
/tenants with valid input creates the tenant, workspace, and workspace member records in the database and returns a 200 response with the tenant data.
- A GET request to
/tenants/me by an authenticated user returns the correct tenant information for that user.
- An authenticated user cannot call
POST /tenants a second time.
- A request to
/tenants/me without a valid JWT token fails with an authentication error.
Dependencies: 2
Ticket 3.5: User Profile API Router
Objective: Implement API endpoints for retrieving and updating the current user's profile.
Context: This provides the basic user profile management functionality. It allows users to view their own information and update mutable fields like their display name, which is a standard feature for any multi-user application.
Technical Directives:
- Create the user router in
backend/control_plane/routers/users.py.
- Both endpoints must use the
get_current_user dependency for authentication and to identify the user.
- Implement the
GET /users/me endpoint to query and return the User record matching the user_id from the JWT claims.
- Implement the
PATCH /users/me endpoint to update the display_name of the current user.
Scope Boundaries:
- FastAPI router module (
users.py)
Acceptance Criteria:
- A GET request to
/users/me by an authenticated user returns their own user profile data.
- A PATCH request to
/users/me with a new display_name updates the corresponding record in the database and returns the updated user profile.
- A user cannot view or modify another user's profile through these endpoints.
- A request to either endpoint without a valid JWT fails with an authentication error.
Dependencies: 2
Ticket 3.6: Workspace Authorization Logic
Objective: Create a reusable authorization function to enforce role-based access control within workspaces.
Context: This ticket implements the core authorization logic that determines if a user has sufficient permissions to perform an action on a workspace. This function will be used as a dependency in the workspace router to protect endpoints according to the business rules defined in the ADD.
Technical Directives:
- In
backend/control_plane/auth/authorization.py, implement the require_workspace_role function as specified in the phase contract.
- The function must accept a
workspace_id, user_id, minimum_role, and a database session.
- It must query the
workspace_members table to find the user's role for the given workspace.
- It must raise an
AuthorizationError if the user is not a member of the workspace or if their role is lower than the minimum_role.
- The role hierarchy is
viewer < editor < owner.
Scope Boundaries:
- Authorization logic module
Acceptance Criteria:
- The function does not raise an error if the user's role meets or exceeds the minimum requirement.
- The function raises an
AuthorizationError if the user's role is below the minimum requirement (e.g., a viewer attempting an editor action).
- The function raises an
AuthorizationError if the user is not a member of the specified workspace.
Dependencies: 2
Ticket 3.7: Workspace Management API Router
Objective: Implement the API endpoints for creating, listing, and retrieving workspaces.
Context: This ticket delivers the core functionality for managing workspaces, which are the primary containers for sessions. It uses the authentication middleware from Ticket 2 and the authorization logic from Ticket 6 to ensure all operations are secure and correctly scoped to the user's tenant and permissions.
Technical Directives:
- Create the workspace router in
backend/control_plane/routers/workspaces.py.
- Implement
GET /workspaces to list all workspaces within the user's tenant.
- Implement
POST /workspaces to create a new workspace within the user's tenant. The user who creates the workspace must be made its owner.
- Implement
GET /workspaces/{workspace_id} to retrieve a single workspace by its ID.
- The
GET /workspaces/{workspace_id} endpoint must use the require_workspace_role dependency to ensure the user has at least viewer permissions.
- All endpoints must use the
get_current_user dependency.
Scope Boundaries:
- FastAPI router module (
workspaces.py)
Acceptance Criteria:
- A GET request to
/workspaces returns a list of workspaces belonging only to the user's tenant.
- A POST request to
/workspaces creates a new Workspace and WorkspaceMember record and returns the new workspace data.
- A GET request to
/workspaces/{workspace_id} by a user with viewer rights succeeds.
- A GET request to
/workspaces/{workspace_id} by a user who is not a member of that workspace is rejected with an AuthorizationError.
- A request to any endpoint that attempts to access a workspace in another tenant fails.
Dependencies: 2, 6
Ticket 3.8: SES Email Helper Module
Objective: Create a shared module for sending transactional emails via Amazon SES.
Context: This ticket centralizes the logic for sending emails, as required by various parts of the application. The first consumer will be the workspace member invitation flow, but this module is designed to be reused for other notifications as specified in the ADD.
Technical Directives:
- Create the email helper module in
backend/shared/email.py.
- Implement the
send_workspace_invitation function.
- This function must construct and send an email using the AWS SES API.
- If the SES API call fails, the function must log the error and enqueue a message to a dead-letter queue for later inspection/retry. It must not raise an exception that would fail the parent API request.
- Use the
SES_SENDER_ADDRESS environment variable for the "from" address.
Scope Boundaries:
Acceptance Criteria:
- Calling
send_workspace_invitation with a valid recipient address results in an email being sent via SES.
- If the SES service is unavailable (simulated via mocking), the function logs an error, enqueues a message, and returns successfully without raising an exception.
Dependencies: None
Ticket 3.9: Workspace Member Management Endpoints
Objective: Implement API endpoints for inviting and removing workspace members.
Context: This ticket completes the workspace management functionality by allowing workspace owners to manage their team's access. It integrates the authorization logic and the SES email helper to create a secure and user-friendly invitation flow.
Technical Directives:
- Add new endpoints to the workspace router in
backend/control_plane/routers/workspaces.py.
- Implement
POST /workspaces/{workspace_id}/members.
- This endpoint must use the
require_workspace_role dependency to ensure the caller is an owner.
- It must create a
WorkspaceMember record for the invited user.
- It must call the
send_workspace_invitation function from the email helper module.
- Implement
DELETE /workspaces/{workspace_id}/members/{user_id}.
- This endpoint must use the
require_workspace_role dependency to ensure the caller is an owner.
- It must delete the specified
WorkspaceMember record.
Scope Boundaries:
- FastAPI router module (
workspaces.py)
Acceptance Criteria:
- A POST request to
/workspaces/{workspace_id}/members by a workspace owner creates a WorkspaceMember record and sends an invitation email.
- An attempt to invite a member by a non-owner is rejected with an
AuthorizationError.
- A DELETE request to
/workspaces/{workspace_id}/members/{user_id} by an owner removes the corresponding WorkspaceMember record.
- An attempt to remove a member by a non-owner is rejected with an
AuthorizationError.
Dependencies: 7, 8
Phase 4: Document Ingestion Pipeline
Phase 5: Session Management and Worker Lifecycle
Phase 6: LangGraph Orchestration Worker
Phase 7: Frontend Application
Phase 8: GDPR Erasure Workflow and Data Lifecycle
Phase 9: Observability, Hardening, and Production Readiness