Requirements & How they were implemented in AWS
1. Introduction
1.1 Purpose
This User Requirements Specification (URS) defines the functional and non-functional requirements for a Validated Document Control (VDC) system designed to manage controlled documents in a regulated Life Sciences environment with full compliance to GxP, 21 CFR Part 11, and CSV requirements. This document is reverse-engineered from the implemented codebase to ensure accuracy and traceability.
1.2 Scope
This VDC system provides:
- Secure document submission and storage with version control
- Role-based access control (Submitter and Approver roles ONLY - no Admin role)
- Multi-Factor Authentication (MFA) via TOTP for ALL users
- Electronic approval workflow with audit trail
- Immutable audit logging of all system actions
- Data integrity controls aligned to ALCOA+ principles
- MFA-enforced authentication via Amazon Cognito
1.3 Regulatory Context
The system must comply with:
- 21 CFR Part 11: Electronic Records; Electronic Signatures
- GxP: Good Practice quality guidelines
- EU Annex 11: Computerised Systems (where applicable)
- GAMP 5: Risk-based approach to compliant GxP computerized systems
1.4 System User Roles
The system supports exactly two user roles:
- Submitter: Users assigned to Cognito group "Submitter" (submitter1, submitter2 accounts)
- Approver: Users assigned to Cognito group "Approver" (approver1, approver2 accounts)
- NO Admin role exists - system administration is performed via AWS Cognito console by IT administrators
2. User Requirements
2.1 Authentication & Multi-Factor Authentication (MFA) Requirements
Req ID
Requirement
Priority
URS-AUTH-001
Requirement: The system SHALL require Multi-Factor Authentication (TOTP 6-digit code) for ALL users (Submitters and Approvers) at login.
Rationale: Provides strong authentication for all system access per 21 CFR Part 11.10(d) and GxP security requirements. MFA prevents unauthorized access even if credentials are compromised.
Implementation: Cognito User Pool configured with MfaConfiguration: OPTIONAL and EnabledMfas: [SOFTWARE_TOKEN_MFA] in
cloudformation/prod/vdc-prod-identity.yaml. All user accounts (submitter1, submitter2, approver1, approver2) have requiresMFA: true in pages/life-sciences/app/login.js USER_CREDENTIALS. TOTP codes generated via components/TOTPGenerator.js.Critical
URS-AUTH-002
Requirement: The system SHALL authenticate users via Amazon Cognito OAuth 2.0 authorization code flow with Hosted UI.
Rationale: Industry-standard OAuth 2.0 flow provides secure token exchange per 21 CFR Part 11.10(a). Cognito Hosted UI manages password complexity and MFA challenges.
Implementation: Login flow in
pages/life-sciences/app/login.js redirects to Cognito Hosted UI via CONFIG.cognitoDomain/oauth2/authorize. Callback handler in pages/life-sciences/app/callback.js exchanges authorization code for JWT tokens via lib/life_sciences_app_lib/auth.js exchangeCodeForTokens().Critical
URS-AUTH-003
Requirement: The system SHALL enforce password policy: minimum 12 characters, requiring uppercase, lowercase, numbers, and symbols.
Rationale: Strong password policy per 21 CFR Part 11.10(b) and industry security best practices.
Implementation: Cognito User Pool PasswordPolicy configured in
cloudformation/prod/vdc-prod-identity.yaml lines 34-41: MinimumLength: 12, RequireLowercase: true, RequireNumbers: true, RequireSymbols: true, RequireUppercase: true.Critical
URS-AUTH-004
Requirement: The system SHALL use email addresses as usernames and auto-verify email addresses.
Rationale: Email-based authentication provides unique user identification and enables account recovery per 21 CFR Part 11.10(c).
Implementation: Cognito User Pool UsernameAttributes: [email] and AutoVerifiedAttributes: [email] in
cloudformation/prod/vdc-prod-identity.yaml lines 30-33.Critical
URS-AUTH-005
Requirement: The system SHALL store JWT tokens (ID token, Access token) in browser localStorage and validate token expiration before API calls.
Rationale: Secure token storage and validation prevents use of expired credentials per 21 CFR Part 11.10(d).
Implementation: Token storage in
lib/life_sciences_app_lib/auth.js setTokens() stores to localStorage. Token expiration check in isExpired() validates exp claim with 60-second buffer. getAccessToken() clears expired tokens automatically.Critical
URS-AUTH-006
Requirement: The system SHALL enable Cognito Advanced Security Mode (ENFORCED) for enhanced threat detection.
Rationale: Advanced security provides protection against compromised credentials and brute force attacks per GxP security requirements.
Implementation: UserPoolAddOns.AdvancedSecurityMode: ENFORCED in
cloudformation/prod/vdc-prod-identity.yaml line 55.Critical
2.2 Role-Based Access Control (RBAC) Requirements
Req ID
Requirement
Priority
URS-RBAC-001
Requirement: The system SHALL support exactly two user roles: Submitter and Approver. NO Admin role SHALL exist in the application.
Rationale: Implements role-based access control and separation of duties per 21 CFR Part 11.10(g). Admin functions are performed via AWS Cognito console by IT administrators only.
Implementation: Role determination in
lib/life_sciences_app_lib/auth.js getUserFromToken() extracts cognito:groups from JWT ID token. Default role is "Submitter" if no groups present. Role set to "Approver" only if groups.includes("Approver"). No "Admin" role logic exists in codebase.Critical
URS-RBAC-002
Requirement: Submitter role users (submitter1, submitter2) SHALL be able to upload documents but SHALL NOT be able to approve or reject documents.
Rationale: Separation of duties prevents submitters from approving their own work per 21 CFR Part 11.10(g) and GxP quality requirements.
Implementation: Frontend role check in
pages/life-sciences/app/approval/[id].js line 63 validates roleIsApprover(u.role). Backend Lambda functions (approve, reject) validate cognito:groups claim contains "Approver" group. Submitter group users receive 403 Forbidden if attempting approval actions.Critical
URS-RBAC-003
Requirement: Approver role users (approver1, approver2) SHALL be able to approve/reject documents but SHALL NOT be able to upload documents.
Rationale: Role separation ensures approvers focus on review activities. Document submission is restricted to Submitter role per workflow design.
Implementation: Upload Lambda functions validate user has Submitter group membership. Approver-only users attempting upload receive appropriate error. Frontend
pages/life-sciences/app/upload.js accessible to all authenticated users, but backend enforces role-based submission.Critical
URS-RBAC-004
Requirement: The system SHALL prevent Submitters from viewing the Pending Approvals page and SHALL display an appropriate message.
Rationale: Prevents unauthorized access to approval queue per role-based access control principles.
Implementation: Frontend check in
pages/life-sciences/app/submissions.js onClickPendingApprovals() displays "Only Approvers can view Pending Approval documents" message. Navigation link disabled for non-approvers via disabledNav class.Critical
URS-RBAC-005
Requirement: The system SHALL prevent users from approving their own submitted documents (self-approval prevention).
Rationale: Critical separation of duties control per 21 CFR Part 11.10(g). Prevents conflict of interest where submitter approves own work.
Implementation: Approve Lambda function validates submittedBy !== currentUser before allowing approval. If match detected, returns 403 Forbidden with error message. Tested in OQ-004 and PQ-014 per
pages/life-sciences/docs/inspection-qa.js line 228-230.Critical
URS-RBAC-006
Requirement: The system SHALL extract user role from JWT token cognito:groups claim and enforce role-based access at both frontend and backend layers.
Rationale: Defense-in-depth security approach. Frontend provides user experience, backend enforces security per 21 CFR Part 11.10(g).
Implementation: Frontend:
lib/life_sciences_app_lib/auth.js getUserFromToken() parses JWT and extracts cognito:groups. Backend: API Gateway JWT Authorizer validates token and passes claims to Lambda. Lambda functions check event.requestContext.authorizer.claims["cognito:groups"] array.Critical
2.3 Document Submission Requirements
Req ID
Requirement
Priority
URS-SUBMIT-001
Requirement: The system SHALL allow authorized Submitters to upload documents for approval via a three-step process: (1) upload-init, (2) direct S3 PUT, (3) submit.
Rationale: Secure document upload workflow per 21 CFR Part 11.10(a). Presigned URLs enable direct S3 upload without exposing credentials.
Implementation: Frontend workflow in
pages/life-sciences/app/upload.js handleSubmit() calls POST /documents/upload/init, then PUT to presignedUrl, then POST /documents/submit. UploadInitLambda generates presigned S3 URL with 15-minute expiration.Critical
URS-SUBMIT-002
Requirement: The system SHALL generate a unique document ID (UUID v4) for each uploaded document before S3 upload.
Rationale: Unique document identification per 21 CFR Part 11.10(e) enables traceability and prevents document conflicts.
Implementation: UploadInitLambda generates documentId = str(uuid.uuid4()) in
temp-analysis/vdc-dev-template.yaml line 200. Document ID is returned to the frontend before S3 upload and used as part of the S3 key prefix in the pattern <env>/documents/{document_id}/{filename}, where <env> represents the environment (for example, dev or prod).Critical
URS-SUBMIT-003
Requirement: The system SHALL calculate and store SHA-256 hash for document integrity verification during the submit step (after S3 upload completes).
Rationale: Data integrity verification per 21 CFR Part 11.10(e) and ALCOA+ Accurate principle. Hash enables detection of document tampering.
Implementation: SubmitLambda reads uploaded file from S3 and calculates SHA-256 hash via sha256_stream() function. Hash stored in DynamoDB document metadata (sha256 field) and audit trail (integrity.sha256). Hash verification on download ensures document integrity.
Critical
URS-SUBMIT-004
Requirement: The system SHALL record submission timestamp in ISO 8601 format (UTC) at the time of document submission.
Rationale: Contemporaneous record-keeping per 21 CFR Part 11.10(e) and ALCOA+ Contemporaneous principle. UTC ensures timezone-independent auditability.
Implementation: SubmitLambda sets submittedAt = utc_now_iso() in DynamoDB document metadata. Timestamp format: ISO 8601 with milliseconds and Z suffix (e.g., "2026-01-27T23:44:35.394527Z"). Displayed in human-readable format via
lib/life_sciences_app_lib/utils.js formatUtcTimestamp().Critical
URS-SUBMIT-005
Requirement: The system SHALL store documents in encrypted S3 storage (AES-256 Server-Side Encryption) with versioning enabled.
Rationale: Data protection at rest per 21 CFR Part 11.10(b) and encryption requirements. Versioning enables document history recovery.
Implementation: S3 bucket VdcDocsBucket configured with BucketEncryption.ServerSideEncryptionByDefault.SSEAlgorithm: AES256 and VersioningConfiguration.Status: Enabled in
cloudformation/prod/vdc-prod-app.yaml lines 65-70.Critical
URS-SUBMIT-006
Requirement: The system SHALL store document metadata in DynamoDB with composite key: pk="DOC#{documentId}", sk="METADATA".
Rationale: Structured data storage enables efficient querying and document retrieval per system design requirements.
Implementation: DynamoDB Documents table schema: pk (HASH), sk (RANGE), gsi1pk, gsi1sk for status-based queries. Document metadata stored with pk=f"DOC#{document_id}", sk="METADATA" in
temp-analysis/vdc-dev-template.yaml lines 210-228.Critical
URS-SUBMIT-007
Requirement: The system SHALL capture submitter identity (email, displayName) from JWT token and store in document metadata as ownerEmail, ownerDisplayName, submittedByEmail.
Rationale: Attributable record-keeping per 21 CFR Part 11.10(e) and ALCOA+ Attributable principle. Links document to authenticated user.
Implementation: Frontend
pages/life-sciences/app/upload.js sends userIdentity (currentUserLabel) in submit request. SubmitLambda extracts user email from JWT claims and stores in DynamoDB as ownerEmail, submittedByEmail fields. Frontend prioritizes displayName || email over username to avoid UUIDs.Critical
URS-SUBMIT-008
Requirement: The system SHALL require document title as a mandatory field during submission.
Rationale: Document identification and searchability per business requirements.
Implementation: SubmitLambda validates title is required in
temp-analysis/vdc-dev-template.yaml line 322-323. Returns 400 error if title missing. Frontend pages/life-sciences/app/upload.js requires docName input before submission.Critical
URS-SUBMIT-009
Requirement: The system SHALL set document status to "DRAFT" during upload-init and "SUBMITTED" after successful submit.
Rationale: Workflow state management enables proper document lifecycle tracking per business process requirements.
Implementation: UploadInitLambda creates document with status="DRAFT" in DynamoDB. SubmitLambda validates status must be "DRAFT" before submission (line 329), then updates status to "SUBMITTED" via UpdateItem expression. Status stored in gsi1pk="STATUS#SUBMITTED" for querying pending approvals.
Critical
2.4 Approval Workflow Requirements
Req ID
Requirement
Priority
URS-APPROVE-001
Requirement: The system SHALL allow authorized Approvers to view pending approval requests via GET /approvals/pending endpoint.
Rationale: Approver access to pending documents per workflow requirements and 21 CFR Part 11.10(g).
Implementation: PendingApprovalsLambda queries DynamoDB GSI1 where gsi1pk="STATUS#SUBMITTED" to retrieve all submitted documents. Frontend
pages/life-sciences/app/approval/approvals/index.js displays pending documents in table format. API route: GET /approvals/pending with JWT authorization required.Critical
URS-APPROVE-002
Requirement: The system SHALL display document metadata (ID, filename, submitter email/name, submission timestamp, SHA-256 hash) to Approvers for review.
Rationale: Complete document information enables informed approval decisions per 21 CFR Part 11.10(e) and audit requirements.
Implementation: Frontend
pages/life-sciences/app/approval/[id].js displays document summary with title, documentId, submittedBy (prioritizing email/displayName over UUIDs), submittedAt (human-readable UTC), and status. Metadata retrieved from DynamoDB document record.Critical
URS-APPROVE-003
Requirement: The system SHALL allow Approvers to download and review documents via controlled copy (presigned S3 URL with 5-minute expiration) before making approval decision.
Rationale: Document review capability per workflow requirements. Short-lived presigned URLs enhance security per 21 CFR Part 11.10(b).
Implementation: DownloadLambda generates presigned S3 URL with ExpiresIn=300 (5 minutes) in
cloudformation/prod/vdc-prod-app.yaml DownloadLambda configuration. Frontend pages/life-sciences/app/approval/[id].js openControlledCopy() calls GET /documents/:id/download and opens URL in new window.Critical
URS-APPROVE-004
Requirement: The system SHALL provide Approve action (POST /approvals/:id/approve) that requires MFA-authenticated session and Approver role.
Rationale: Electronic signature via MFA-authenticated approval per 21 CFR Part 11.10(g) and 11.50. Approval action represents electronic signature with meaning equivalent to handwritten signature.
Implementation: ApproveLambda validates user has "Approver" group in cognito:groups claim. ENFORCE_APPROVER_MFA environment variable enables MFA validation. Frontend
pages/life-sciences/app/approval/[id].js handleApprove() calls API endpoint. Approval creates audit record and updates document status to "APPROVED".Critical
URS-APPROVE-005
Requirement: The system SHALL provide Reject action (POST /approvals/:id/reject) that requires MFA-authenticated session, Approver role, and mandatory rejection reason.
Rationale: Rejection with reason provides audit trail justification per 21 CFR Part 11.10(e). Rejection reason recorded in audit trail for FDA inspection.
Implementation: RejectLambda validates Approver role and requires comment field in request body. Frontend
pages/life-sciences/app/approval/[id].js handleReject() validates rejectionComment.trim() is not empty before API call. Rejection reason stored in audit trail details.comment field. Document status updated to "REJECTED".Critical
URS-APPROVE-006
Requirement: The system SHALL prevent self-approval by validating submittedBy (from document metadata) does not equal currentUser (from JWT token) before allowing approval.
Rationale: Separation of duties per 21 CFR Part 11.10(g). Prevents conflict of interest where submitter approves own document.
Implementation: ApproveLambda compares document.submittedBy (or ownerEmail) with JWT claims.email. If match detected, returns 403 Forbidden error. Tested in OQ-004 and PQ-014. Frontend also validates roleIsApprover() but backend enforcement is authoritative.
Critical
URS-APPROVE-007
Requirement: The system SHALL update document status from "SUBMITTED" to "APPROVED" or "REJECTED" upon approver decision.
Rationale: Workflow state management enables proper document lifecycle tracking and status visibility per business requirements.
Implementation: ApproveLambda and RejectLambda update DynamoDB document metadata status field via UpdateItem. GSI1 keys updated: gsi1pk="STATUS#APPROVED" or "STATUS#REJECTED", gsi1sk=timestamp for querying by status. Status displayed in frontend with color-coded badges.
Critical
2.5 Audit Trail Requirements (21 CFR Part 11)
Req ID
Requirement
Priority
URS-AUDIT-001
Requirement: The system SHALL create audit records for all document upload-init actions with eventType "DOC_UPLOAD_INITIATED".
Rationale: Complete audit trail per 21 CFR Part 11.10(e). Records document creation initiation for traceability.
Implementation: UploadInitLambda creates audit record in VdcAuditTable with eventType="DOC_UPLOAD_INITIATED", timestampUtc, actorUserId, actorUsername, details (filename, contentType), and integrity (S3 bucket/key) in
temp-analysis/vdc-dev-template.yaml lines 231-243.Critical
URS-AUDIT-002
Requirement: The system SHALL create audit records for all document submissions with eventType "SUBMIT" and electronic signature record with signatureMeaning "SUBMIT".
Rationale: Submission audit record and electronic signature per 21 CFR Part 11.50 and 11.70. Signature record provides attestation text.
Implementation: SubmitLambda creates two DynamoDB records: (1) Audit record with eventType="SUBMIT", (2) Electronic signature record with sk=f"ESIG#{timestamp}#{sig_id}", signatureMeaning="SUBMIT", attestationText="I attest this submission is accurate and complete." in
temp-analysis/vdc-dev-template.yaml lines 345-388.Critical
URS-AUDIT-003
Requirement: The system SHALL create audit records for all approval actions with eventType "APPROVE", including approver identity and timestamp.
Rationale: Approval audit record per 21 CFR Part 11.10(e) and 11.50. Records who approved, when, and which document.
Implementation: ApproveLambda creates audit record with eventType="APPROVE", timestampUtc, actorEmail (from JWT), actorUsername, documentId, and outcome="success". Record stored in VdcAuditTable with composite key: docId (documentId), eventKey (timestamp-based).
Critical
URS-AUDIT-004
Requirement: The system SHALL create audit records for all rejection actions with eventType "REJECT", including approver identity, timestamp, and rejection reason in details.comment.
Rationale: Rejection audit record with reason per 21 CFR Part 11.10(e). Enables FDA inspection to understand why documents were rejected.
Implementation: RejectLambda creates audit record with eventType="REJECT", timestampUtc, actorEmail, details.comment (rejection reason from request body), and outcome="success". Rejection reason displayed in audit trail via
pages/life-sciences/app/documents/[id].js formatAction() function.Critical
URS-AUDIT-005
Requirement: Audit records SHALL include: eventId (UUID), documentId, timestampUtc (ISO 8601), eventType, actorEmail, actorUsername, actorUserId, actorGroups, details (action-specific), and integrity (S3 bucket/key/SHA-256).
Rationale: Complete audit record per 21 CFR Part 11.10(e) and ALCOA+ principles. Enables full traceability of who did what, when, and on which document.
Implementation: Audit record schema in DynamoDB VdcAuditTable: docId (HASH), eventKey (RANGE with timestamp). Fields include eventId, timestampUtc, eventType, actorEmail, actorUsername, actorUserId, actorGroups (from JWT cognito:groups), details (JSON object), integrity (S3 location and SHA-256 hash).
Critical
URS-AUDIT-006
Requirement: Audit records SHALL be immutable (stored in DynamoDB with IAM policies that deny UpdateItem and DeleteItem operations).
Rationale: Immutability per 21 CFR Part 11.10(e) prevents tampering with audit records. Only PutItem (create) and Query (read) operations permitted.
Implementation: VdcLambdaRole IAM policy in
cloudformation/prod/vdc-prod-app.yaml lines 240-246 allows only dynamodb:PutItem, dynamodb:Query, dynamodb:GetItem on VdcAuditTable. No UpdateItem or DeleteItem permissions granted. Tested in OQ-017 per pages/life-sciences/docs/inspection-qa.js lines 113-118.Critical
URS-AUDIT-007
Requirement: The system SHALL provide audit trail retrieval capability via GET /documents/:id/audit endpoint, returning events sorted chronologically.
Rationale: Audit trail inspection capability per 21 CFR Part 11.10(e) and FDA inspection requirements. Enables retrieval of complete document history.
Implementation: DocumentAuditLambda queries VdcAuditTable by docId (documentId) and returns events array. Frontend
pages/life-sciences/app/documents/[id].js displays audit trail in FDA-ready format matching Overview page example: "timestamp | action | Actor: name". Events sorted by timestamp (oldest first).Critical
URS-AUDIT-008
Requirement: The system SHALL store audit records in a separate DynamoDB table (VdcAuditTable) with composite key: docId (HASH), eventKey (RANGE).
Rationale: Separation of audit data from operational data per security best practices. Composite key enables efficient querying by document.
Implementation: VdcAuditTable schema in
cloudformation/prod/vdc-prod-app.yaml lines 181-199: docId (HASH), eventKey (RANGE with format "timestamp#eventId"). Table name: VDC_Audit_{EnvironmentName}. Point-in-time recovery enabled for 7-year retention.Critical
URS-AUDIT-009
Requirement: The system SHALL display audit trail in FDA-ready format: "AUDIT TRAIL (UTC)" header, document metadata (Title, ID, Status), "EVENTS (UTC)" section with format "timestamp | action | Actor: name".
Rationale: Human-readable audit trail format per FDA inspection requirements. Matches example format shown on Overview page for consistency.
Implementation: Frontend
pages/life-sciences/app/documents/[id].js auditTrailText useMemo formats audit events as: "{timestamp} | {action} | Actor: {actor}". Timestamps use ISO 8601 format with Z suffix via formatUtcTimestampForAudit(). Rejection reasons included in action text: "Rejected: {reason}".Critical
URS-AUDIT-010
Requirement: The system SHALL write audit records to S3 WORM (Write-Once-Read-Many) bucket with Object Lock in COMPLIANCE mode for long-term retention.
Rationale: Immutable long-term audit storage per 21 CFR Part 11.10(e) and 7-year retention requirements. COMPLIANCE mode prevents deletion even by root account.
Implementation: VdcAuditWormBucket is configured with ObjectLockEnabled: true and ObjectLockConfiguration with Mode: COMPLIANCE and DefaultRetention: 90 days in
cloudformation/prod/vdc-prod-app.yaml lines 122-143. Lambda functions write audit JSON to S3 under an environment-specific prefix such as audit/<env>/ (for example, audit/dev/ or audit/prod/).Critical
2.6 Electronic Signature Requirements (21 CFR Part 11)
Req ID
Requirement
Priority
URS-ESIG-001
Requirement: The system SHALL create electronic signature records for document submission with signatureMeaning "SUBMIT", signerUserId, signerUsername, timestampUtc, and attestationText.
Rationale: Electronic signature per 21 CFR Part 11.50 and 11.70. Submission represents electronic signature with meaning equivalent to handwritten signature.
Implementation: SubmitLambda creates electronic signature record in DynamoDB with sk=f"ESIG#{timestamp}#{sig_id}", signatureMeaning="SUBMIT", signerRole="UPLOADER", attestationText="I attest this submission is accurate and complete." in
temp-analysis/vdc-dev-template.yaml lines 371-388.Critical
URS-ESIG-002
Requirement: The system SHALL create electronic signature records for approval actions with signatureMeaning "APPROVE", including approver identity from MFA-authenticated JWT token.
Rationale: Electronic signature for approval per 21 CFR Part 11.50. MFA-authenticated session provides two-factor authentication equivalent to handwritten signature.
Implementation: ApproveLambda creates electronic signature record with signerEmail, signerUsername from JWT claims, signatureMeaning="APPROVE", timestampUtc, and documentId. Signature linked to document via pk="DOC#{documentId}".
Critical
URS-ESIG-003
Requirement: Electronic signatures SHALL be linked to the signer's identity (email, username, userId) from the authenticated JWT token, not from user input.
Rationale: Prevents signature forgery per 21 CFR Part 11.50(b). Identity must come from authenticated session, not user-provided data.
Implementation: Lambda functions extract user identity from event.requestContext.authorizer.claims (email, sub, cognito:username) passed by API Gateway JWT Authorizer. No user-provided identity fields accepted. JWT token validated by API Gateway before reaching Lambda.
Critical
URS-ESIG-004
Requirement: Electronic signatures SHALL include timestamp in ISO 8601 UTC format recorded at the time of signature action.
Rationale: Timestamped signatures per 21 CFR Part 11.50(a). UTC ensures timezone-independent auditability.
Implementation: All signature records include timestampUtc field in ISO 8601 format with milliseconds and Z suffix (e.g., "2026-01-27T23:44:35.394527Z"). Timestamp generated server-side at action time, not from client.
Critical
2.7 Data Integrity Requirements (ALCOA+)
Req ID
Requirement
Priority
URS-INTEGRITY-001
Requirement: Attributable: All actions SHALL be linked to authenticated user identity (email, userId from JWT token cognito:groups claim).
Rationale: ALCOA+ Attributable principle per 21 CFR Part 11.10(e). Every action must be traceable to a specific authenticated user.
Implementation: All audit records include actorEmail, actorUsername, actorUserId extracted from JWT token claims. Frontend
lib/life_sciences_app_lib/auth.js getUserFromToken() extracts email and sub from ID token. Backend Lambda functions use event.requestContext.authorizer.claims for user identity.Critical
URS-INTEGRITY-002
Requirement: Legible: All records SHALL be human-readable and stored in UTF-8 encoding.
Rationale: ALCOA+ Legible principle per 21 CFR Part 11.10(e). Records must be readable by humans and systems.
Implementation: DynamoDB stores all text fields as UTF-8 strings. Audit trail displayed in human-readable format via
pages/life-sciences/app/documents/[id].js with monospace font. Frontend prioritizes human-readable fields (email, displayName) over UUIDs in owner display logic.Critical
URS-INTEGRITY-003
Requirement: Contemporaneous: Timestamps SHALL be recorded at the time of action (server-side, not client-side).
Rationale: ALCOA+ Contemporaneous principle per 21 CFR Part 11.10(e). Prevents timestamp manipulation by recording server-side.
Implementation: All Lambda functions generate timestamps server-side using utc_now_iso() function. No client-provided timestamps accepted. Timestamps stored in ISO 8601 format with milliseconds: "2026-01-27T23:44:35.394527Z".
Critical
URS-INTEGRITY-004
Requirement: Original: Documents SHALL be stored in original uploaded format without modification.
Rationale: ALCOA+ Original principle per 21 CFR Part 11.10(e). Documents must be stored exactly as uploaded to preserve authenticity.
Implementation: S3 stores documents with original filename and content-type. No document transformation or conversion performed. S3 versioning enabled to preserve original versions. Documents retrieved via presigned URLs maintain original format.
Critical
URS-INTEGRITY-005
Requirement: Accurate: SHA-256 hashes SHALL verify document integrity on upload and enable verification on download.
Rationale: ALCOA+ Accurate principle per 21 CFR Part 11.10(e). Hash verification detects document tampering or corruption.
Implementation: SubmitLambda calculates SHA-256 hash via sha256_stream() function after S3 upload completes. Hash stored in DynamoDB document metadata (sha256 field) and audit trail (integrity.sha256). Download verification can compare stored hash with recalculated hash to detect tampering.
Critical
URS-INTEGRITY-006
Requirement: Complete: All required document metadata fields (title, documentId, submittedAt, sha256, status, ownerEmail) SHALL be captured and stored.
Rationale: ALCOA+ Complete principle. All necessary information for document traceability must be captured.
Implementation: SubmitLambda validates required fields (title, documentId) and stores complete metadata in DynamoDB: title, description, documentId, submittedAt, sha256, status, ownerEmail, ownerDisplayName, s3Bucket, s3Key, s3VersionId, contentType, originalFilename.
Critical
URS-INTEGRITY-007
Requirement: Consistent: Document status transitions SHALL follow defined workflow: DRAFT → SUBMITTED → (APPROVED | REJECTED).
Rationale: ALCOA+ Consistent principle. Workflow state management ensures consistent document lifecycle.
Implementation: SubmitLambda validates document status must be "DRAFT" before allowing submission (line 329). Status updated to "SUBMITTED" only from "DRAFT". ApproveLambda/RejectLambda update status to "APPROVED" or "REJECTED" only from "SUBMITTED". Status stored in gsi1pk for querying.
Critical
URS-INTEGRITY-008
Requirement: Enduring: Documents and audit records SHALL be retained for minimum 7 years with Point-in-Time Recovery enabled.
Rationale: ALCOA+ Enduring principle and 21 CFR Part 11.10(e) retention requirements. Long-term data preservation for FDA inspection.
Implementation: DynamoDB tables (VdcDocumentsTable, VdcAuditTable) configured with PointInTimeRecoverySpecification.PointInTimeRecoveryEnabled: true in
cloudformation/prod/vdc-prod-app.yaml lines 178 and 199. S3 audit WORM bucket with Object Lock provides additional long-term storage.Critical
2.8 Security & Encryption Requirements
Req ID
Requirement
Priority
URS-SEC-001
Requirement: The system SHALL encrypt data in transit using TLS 1.2+ on all API Gateway and CloudFront endpoints.
Rationale: Data protection in transit per 21 CFR Part 11.10(b) and industry security standards.
Implementation: API Gateway HTTP API enforces TLS 1.2+ by default. CloudFront distribution uses TLS for all frontend traffic. All API calls use HTTPS. CORS configuration allows only authorized origins.
Critical
URS-SEC-002
Requirement: The system SHALL encrypt data at rest using AES-256 Server-Side Encryption for S3 buckets and AWS-managed encryption for DynamoDB tables.
Rationale: Data protection at rest per 21 CFR Part 11.10(b) and encryption requirements.
Implementation: S3 buckets configured with BucketEncryption.ServerSideEncryptionByDefault.SSEAlgorithm: AES256 in
cloudformation/prod/vdc-prod-app.yaml lines 68-70 and 136-138. DynamoDB tables configured with SSESpecification.SSEEnabled: true (AWS-managed keys) in lines 177 and 197.Critical
URS-SEC-003
Requirement: The system SHALL enforce API Gateway JWT Authorizer on all API routes, validating JWT token signature, issuer, and audience before allowing Lambda invocation.
Rationale: API security per 21 CFR Part 11.10(b). JWT validation ensures only authenticated users can access API endpoints.
Implementation: VdcJwtAuthorizer configured in
cloudformation/prod/vdc-prod-app.yaml lines 464-475 with AuthorizerType: JWT, IdentitySource: ["$request.header.Authorization"], JwtConfiguration with Audience (CognitoClientId) and Issuer (CognitoIssuerUrl). All routes (lines 538-608) require AuthorizationType: JWT and AuthorizerId.Critical
URS-SEC-004
Requirement: The system SHALL implement least-privilege IAM policies for Lambda functions, granting only necessary DynamoDB and S3 permissions.
Rationale: Security best practice per GxP requirements. Minimizes attack surface by restricting Lambda permissions to minimum required.
Implementation: VdcLambdaRole IAM policy in
cloudformation/prod/vdc-prod-app.yaml lines 219-267 grants: Documents table (GetItem, PutItem, UpdateItem, Query, Scan), Audit table (PutItem, Query, GetItem only - no Update/Delete), S3 docs bucket (GetObject, PutObject, ListBucket), S3 audit WORM bucket (PutObject, ListBucket). No wildcard permissions.Critical
URS-SEC-005
Requirement: The system SHALL block public access to S3 buckets (BlockPublicAcls, IgnorePublicAcls, BlockPublicPolicy, RestrictPublicBuckets all set to true).
Rationale: S3 security best practice. Prevents accidental public exposure of documents per 21 CFR Part 11.10(b).
Implementation: VdcDocsBucket and VdcAuditWormBucket configured with PublicAccessBlockConfiguration setting all four flags to true in
cloudformation/prod/vdc-prod-app.yaml lines 71-75 and 139-143.Critical
URS-SEC-006
Requirement: The system SHALL configure S3 CORS to allow only authorized frontend origins (explicit domain list, no wildcards).
Rationale: CORS security per web security best practices. S3 does not support wildcard origins, requiring explicit domain list.
Implementation: VdcDocsBucket CorsConfiguration in
cloudformation/prod/vdc-prod-app.yaml lines 76-96 explicitly lists AllowedOrigins: ["https://vdc-mfa-demo.vercel.app", "https://vdc-prod.vercel.app", "https://williamoconnellpmp.com", "http://localhost:3000"]. No wildcard patterns used.Critical
2.9 Document Retrieval & Display Requirements
Req ID
Requirement
Priority
URS-DISPLAY-001
Requirement: The system SHALL display human-readable submitter/owner names (email, displayName) instead of UUIDs in all document listings and detail pages.
Rationale: User experience and audit trail readability. UUIDs are not meaningful to users or FDA inspectors.
Implementation: Frontend functions in
pages/life-sciences/app/documents/index.js pickOwner() and pages/life-sciences/app/submissions.js pickSubmittedBy() prioritize human-readable fields (ownerEmail, ownerDisplayName, submittedByEmail) over UUIDs. UUIDs (long strings > 30 characters without an @) are hidden and display "—" instead.Critical
URS-DISPLAY-002
Requirement: The system SHALL display UTC timestamps in human-readable format (e.g., "Jan 27, 2026 at 11:44 PM UTC") in all user-facing displays, while maintaining ISO 8601 format in audit trail text.
Rationale: User experience for readability while maintaining machine-readable format in audit records per FDA requirements.
Implementation: Frontend
lib/life_sciences_app_lib/utils.js formatUtcTimestamp() formats for display. formatUtcTimestampForAudit() maintains ISO 8601 format for audit trail. All timestamp displays use formatUtcTimestamp() except audit trail pre-formatted text which uses ISO format.Critical
URS-DISPLAY-003
Requirement: The system SHALL provide "Audit Trail" link (not "View") on all document listing pages for consistency and FDA readiness.
Rationale: Consistent terminology per FDA inspection requirements. "Audit Trail" is the standard term for regulatory compliance.
Implementation: Frontend pages use "Audit Trail" link text:
pages/life-sciences/app/submissions.js line 507, pages/life-sciences/app/documents/index.js line 478, pages/life-sciences/app/approval/index.js line 320. Links navigate to /life-sciences/app/documents/:id which displays audit trail section.Critical
URS-DISPLAY-004
Requirement: The system SHALL provide graceful degradation when audit API fails, displaying audit trail built from document metadata instead of showing only error message.
Rationale: User experience and audit trail availability. Even if dedicated audit endpoint fails, document metadata provides basic audit information.
Implementation: Frontend
pages/life-sciences/app/documents/[id].js auditTrailText useMemo builds fallback audit trail from document metadata (submittedAt, status, updatedAt) when audit.length === 0. Error notice displayed but audit trail still shown. Graceful degradation ensures users always see audit information.Critical
3. Non-Functional Requirements
3.1 Performance
- URS-PERF-001: Document upload SHALL complete within 30 seconds for files up to 10MB
- URS-PERF-002: Approval list page SHALL load within 3 seconds
- URS-PERF-003: System SHALL support 100 concurrent users
- URS-PERF-004: Lambda functions SHALL have timeout of 15 seconds (30 seconds for submit/download operations)
3.2 Availability
- URS-AVAIL-001: System SHALL maintain 99.5% uptime during business hours
- URS-AVAIL-002: System SHALL provide graceful error messages for failures
- URS-AVAIL-003: System SHALL use AWS serverless architecture (Lambda, API Gateway) for automatic scaling
3.3 Backup & Recovery
- URS-BACKUP-001: Document storage SHALL be backed up via S3 versioning with Point-in-Time Recovery enabled
- URS-BACKUP-002: Audit logs SHALL be retained for minimum 7 years in S3 WORM bucket with Object Lock
- URS-BACKUP-003: DynamoDB tables SHALL have PointInTimeRecoveryEnabled: true for 35-day recovery window
4. System Architecture
The VDC system is built on AWS serverless architecture:
- Frontend: Next.js static site hosted on Vercel (deployed to vdc-mfa-demo.vercel.app)
- Authentication: Amazon Cognito User Pool with TOTP MFA (SOFTWARE_TOKEN_MFA) for ALL users
- API: API Gateway HTTP API with JWT Authorizer + AWS Lambda functions (Python 3.12)
- Storage: Amazon S3 (documents with AES-256 SSE), DynamoDB (metadata + audit with AWS-managed encryption)
- Audit Storage: S3 WORM bucket with Object Lock (COMPLIANCE mode) for long-term audit retention
- Monitoring: CloudWatch Logs + CloudWatch Alarms
4.1 Lambda Functions
- vdc-upload-init-prod: Generates documentId, S3 presigned URL, creates DRAFT document record
- vdc-submit-prod: Calculates SHA-256 hash, updates document to SUBMITTED, creates audit + e-signature records
- vdc-approvals-pending-prod: Queries SUBMITTED documents for approver review queue
- vdc-approve-prod: Validates Approver role and MFA, prevents self-approval, updates status to APPROVED, creates audit record
- vdc-reject-prod: Validates Approver role and MFA, requires rejection reason, updates status to REJECTED, creates audit record with reason
- vdc-download-prod: Generates presigned S3 URL (5-minute expiration) for controlled document access
- vdc-documents-list-prod: Returns all documents for authenticated user (role-based filtering)
- vdc-document-audit-prod: Queries audit records by documentId, returns chronologically sorted events
4.2 DynamoDB Schema
VDC_Documents_{EnvironmentName} Table:
- Primary Key: pk (HASH), sk (RANGE)
- GSI1: gsi1pk (HASH), gsi1sk (RANGE) - for status-based queries
- Document metadata: pk="DOC#{documentId}", sk="METADATA"
- Electronic signatures: pk="DOC#{documentId}", sk="ESIG#{timestamp}#{sigId}"
- Fields: documentId, title, description, status, submittedAt, sha256, ownerEmail, ownerDisplayName, s3Bucket, s3Key, s3VersionId, contentType
VDC_Audit_{EnvironmentName} Table:
- Primary Key: docId (HASH), eventKey (RANGE)
- Fields: eventId, timestampUtc, eventType, actorEmail, actorUsername, actorUserId, actorGroups, details (JSON), integrity (S3 location + SHA-256)
- Immutable: IAM policy allows only PutItem, Query, GetItem (no UpdateItem, DeleteItem)
4.3 Cognito Configuration
- User Pool: Email-based authentication, password policy (12+ chars, complexity), MFA OPTIONAL (enforced per user), Advanced Security ENFORCED
- Groups: "Submitter" group (submitter1, submitter2), "Approver" group (approver1, approver2)
- MFA: SOFTWARE_TOKEN_MFA (TOTP 6-digit codes) enabled for all users
- Hosted UI: OAuth 2.0 authorization code flow, scopes: openid, email, profile
5. Traceability
All requirements in this URS are traced to:
- Functional Specification: Design specifications implementing each requirement
- Test Cases: IQ/OQ/PQ test protocols validating each requirement
- Traceability Matrix: Complete bidirectional mapping (requirement ↔ test)
- Code Implementation: Specific file paths and line numbers referenced in each requirement
6. Approval
Quality Assurance
William O''Connell
February 1, 2026
System Owner
William O''Connell
February 1, 2026