Operations & Security
This document consolidates security implementation, performance standards, accessibility guidelines, and operational validation for the BATbern Event Management Platform.
Security Requirements
Frontend Security
- CSP Headers: Strict content security policy with CloudFront CDN support
connect-src: Allows connections to self, AWS Cognito (*.amazoncognito.com), the Cognito custom hosted-UI domainauth.batbern.ch(Story 12.9 β Amplify POSTs the OAuth code exchange tohttps://auth.batbern.ch/oauth2/token), CloudFront CDN (*.cloudfront.net), branded CDN domain (cdn.batbern.ch), andapi.batbern.chframe-src: Allows Google Maps embeds (https://www.google.com/maps/) and Cloudflare Turnstile captcha (https://challenges.cloudflare.com)script-src: Allows Cloudflare Turnstile script (https://challenges.cloudflare.com)- Where configured β two places, easy to miss: the SPA documentβs CSP is set by
the frontend CloudFront
ResponseHeadersPolicy(infrastructure/lib/stacks/frontend-stack.ts) β NOT by the gateway Java filters, which only cover API responses (SecurityHeadersFilter.java/SecurityHeadersHandler.java). Lesson from the 2026-06 SSO token-exchange incident: theauth.batbern.chswitch broke federated login in production because only the gateway CSP was considered β any new outbound origin must be added to the frontend-stackconnect-src.
- COEP/CORP: Cross-Origin-Embedder-Policy (COEP) header removed to allow third-party embeds (Google Maps). Cross-Origin-Resource-Policy (CORP) relaxed from
same-origintocross-originfor CDN asset delivery. - CDN SVG hardening (Story 12.12 review, finding #3): users can upload SVGs (profile
pictures, logos) that
cdn.batbern.chserves asimage/svg+xml; an SVG can carry<script>, which executes when its object URL is opened top-level β stored XSS on the cdn origin. The content distributionβsResponseHeadersPolicy(batbern-content-cache-*,infrastructure/lib/stacks/storage-stack.ts) therefore sends CSPsandbox(blocks script execution when the resource IS the document;<img>embedding of PNG/JPEG/WebP/SVG is unaffected) plusX-Content-Type-Options: nosniff. - XSS Prevention: Input sanitization and output encoding
- Secure Storage: Encrypted localStorage for sensitive data
Backend Security
- Input Validation: Comprehensive request validation
- Rate Limiting: API rate limiting per user and endpoint
- CORS Policy: Restrictive CORS configuration
AI / LLM Security (OWASP-LLM, Story 15.9)
- The AI surface lives in
event-management-service(event description, theme image, abstract analysis, trending topics, organizer-editable prompts), is ORGANIZER-gated, AI-feature-flagged (AI_ENABLED, default off), and advisory (output is reviewed before publish; it never drives authorization or control flow). - Mitigations: a fixed non-editable system guard message on every chat call (LLM01/LLM08);
delimiter neutralization in interpolated values; AI output treated as untrusted data (returned as
string / parsed with safe defaults; React auto-escaping on render, never
dangerouslySetInnerHTML); OpenAI key only in theAuthorizationheader;ai_generation_logis hash-only. - Canonical mapping + status:
docs/security/owasp-llm-agentic-checklist.md. - CI: advisory
.github/workflows/ai-security.yml(aiSecurityTest@Tagsubset +scripts/ci/ai-prompt-secret-scan.sh).
Authentication Security
- Token Storage: Secure JWT storage with automatic refresh
- Session Management: Cognito-based session management
- Password Policy: Strong password requirements
- Federated login (Google SSO, ADR-010 β live since 2026-06-04): βContinue with Googleβ
via Cognito OIDC federation on the custom hosted-UI domain
auth.batbern.ch(cognito-stack.tsCustomUserPoolDomain). Existing email/password accounts are linked transparently (AdminLinkProviderForUserin thePreSignUp_ExternalProvidertrigger); brand-new Google users are JIT-provisioned as ATTENDEE and pass the ToS consent gate before reaching protected routes (see06b-user-lifecycle-sync.mdPatterns 1b/F/C). The login button is gated on the runtimefeatures.ssoflag served byGET /api/v1/configβ kill-switch: setFEATURES_SSO_ENABLED=falseon the API gateway and restart (no frontend rebuild). Deactivated accounts are blocked request-time by the gatewayAccountActiveFilter(federation never fires PreAuthentication). - Unconfirmed sign-up recovery: a daily
CognitoConfirmationResendJobre-sends a fresh Cognito confirmation code to accounts unconfirmed past a grace window (Cognitoβs sign-up code is fixed at 24h and not configurable). The CUMS task role is grantedcognito-idp:ListUsers+cognito-idp:ResendConfirmationCode, scoped to the user-pool ARN (the SPA client has no secret, so noSecretHashis needed).
Cost Optimizations (2026-03)
The following cost optimizations were applied to the production (staging) environment:
| Change | Savings | Rationale |
|---|---|---|
| Container Insights V2 disabled | ~$48/month | Not justified for current low traffic volume |
| RDS backup retention reduced 14 β 7 days | ~$4/month | 7 days sufficient for a low-traffic community platform |
| Bastion auto-stop on tunnel close | ~$2/month | start-db-tunnel.sh now automatically stops the bastion EC2 instance when the SSH tunnel is closed |
isProd bug fix (2026-03-22): The cluster-stack.ts and incident-management-stack.ts stacks were incorrectly using envName === 'production' to determine production behavior. Since the consolidated environment uses envName: 'staging' but serves production traffic, this was changed to use config.isProduction (which is true). This ensures production-grade behavior (alerts, scaling) is correctly applied.
Performance Benchmarks and SLAs
Service Level Agreements (SLAs)
Platform Availability SLAs:
| Service Tier | Availability Target | Monthly Downtime | Response Time (P95) | Error Rate |
|---|---|---|---|---|
| Production | 99.9% | < 43 minutes | < 200ms | < 0.1% |
| Development (local) | N/A | N/A | N/A | N/A |
Business SLA Commitments:
- Event Registration: 99.5% availability during registration periods
- Speaker Portal: 99.0% availability during submission deadlines
- Partner Analytics: 98.0% availability for monthly reporting
- Content Discovery: 99.9% availability for public access
Detailed Performance Benchmarks
Frontend Performance Standards:
# Web Vitals Targets (Real User Monitoring)
Core Web Vitals:
First Contentful Paint (FCP):
target: "< 1.5s"
warning: "> 2.0s"
critical: "> 3.0s"
measurement: "75th percentile"
Largest Contentful Paint (LCP):
target: "< 2.5s"
warning: "> 3.0s"
critical: "> 4.0s"
measurement: "75th percentile"
First Input Delay (FID):
target: "< 100ms"
warning: "> 200ms"
critical: "> 300ms"
measurement: "75th percentile"
Cumulative Layout Shift (CLS):
target: "< 0.1"
warning: "> 0.15"
critical: "> 0.25"
measurement: "75th percentile"
Interaction to Next Paint (INP):
target: "< 200ms"
warning: "> 300ms"
critical: "> 500ms"
measurement: "75th percentile"
# Resource Performance
Bundle Performance:
initial_bundle_size:
target: "< 250KB gzipped"
warning: "> 400KB gzipped"
critical: "> 500KB gzipped"
total_bundle_size:
target: "< 1MB gzipped"
warning: "> 1.5MB gzipped"
critical: "> 2MB gzipped"
code_splitting_ratio:
target: "> 80% lazy loaded"
warning: "< 70% lazy loaded"
critical: "< 50% lazy loaded"
# Network Performance
Network Efficiency:
time_to_interactive:
target: "< 3s"
warning: "> 5s"
critical: "> 8s"
lighthouse_performance_score:
target: "> 90"
warning: "< 80"
critical: "< 70"
total_blocking_time:
target: "< 300ms"
warning: "> 600ms"
critical: "> 1000ms"
Backend Service Performance Benchmarks:
# API Gateway Performance
API Gateway:
response_time_p50: "< 50ms"
response_time_p95: "< 200ms"
response_time_p99: "< 500ms"
throughput: "> 1000 req/min"
error_rate: "< 0.1%"
# Domain Service Performance
Event Management Service:
endpoints:
"GET /events":
p95_response_time: "< 150ms"
throughput: "> 500 req/min"
cache_hit_ratio: "> 80%"
"POST /events":
p95_response_time: "< 300ms"
throughput: "> 100 req/min"
validation_time: "< 50ms"
Speaker Coordination Service:
endpoints:
"GET /speakers":
p95_response_time: "< 100ms"
throughput: "> 300 req/min"
database_query_time: "< 30ms"
"POST /speakers/invitations":
p95_response_time: "< 250ms"
throughput: "> 50 req/min"
email_delivery_sla: "< 5 minutes"
Partner Coordination Service:
endpoints:
"GET /partners/{id}/topic-votes":
p95_response_time: "< 200ms"
throughput: "> 50 req/min"
"POST /topics/voting":
p95_response_time: "< 300ms"
throughput: "> 30 req/min"
Attendee Experience Service:
endpoints:
"GET /search":
p95_response_time: "< 300ms"
throughput: "> 500 req/min"
search_relevance_score: "> 85%"
Database Performance Standards:
# PostgreSQL Performance
Database Performance:
connection_pool_utilization: "< 70%"
query_performance:
simple_selects: "< 10ms"
complex_joins: "< 100ms"
aggregations: "< 200ms"
full_text_search: "< 500ms"
indexing_efficiency:
index_hit_ratio: "> 95%"
table_scan_ratio: "< 5%"
replication_lag: "< 100ms"
# Caffeine In-Memory Cache Performance
Cache Performance:
memory_utilization: "< 80%"
hit_ratio: "> 95%"
avg_response_time: "< 1ms (in-process, no network latency)"
eviction_rate: "< 5%"
Accessibility Implementation Guidelines
Accessibility Standards and Compliance
Compliance Targets:
- WCAG 2.1 Level AA: Primary compliance standard for all user interfaces
- Swiss Accessibility Laws: Compliance with Swiss federal accessibility requirements
- Section 508: US accessibility standards for government compatibility
- EN 301 549: European accessibility standard for ICT procurement
Accessibility Audit Schedule:
- Automated Testing: Daily during development with axe-core
- Manual Testing: Weekly accessibility reviews during sprint cycles
- Expert Audit: Quarterly professional accessibility audits
- User Testing: Semi-annual testing with users who have disabilities
Frontend Accessibility Architecture
Core Accessibility Framework
// Accessibility Provider Configuration
import { AccessibilityProvider } from '@/providers/AccessibilityProvider';
interface AccessibilityConfig {
reducedMotion: boolean;
highContrast: boolean;
fontSize: 'small' | 'medium' | 'large' | 'extra-large';
screenReader: boolean;
keyboardNavigation: boolean;
}
const AccessibilityProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [config, setConfig] = useState<AccessibilityConfig>({
reducedMotion: window.matchMedia('(prefers-reduced-motion: reduce)').matches,
highContrast: window.matchMedia('(prefers-contrast: high)').matches,
fontSize: 'medium',
screenReader: false,
keyboardNavigation: true
});
useEffect(() => {
// Detect screen reader usage
const detectScreenReader = () => {
const hasScreenReader = window.speechSynthesis?.speaking ||
navigator.userAgent.includes('NVDA') ||
navigator.userAgent.includes('JAWS');
setConfig(prev => ({ ...prev, screenReader: hasScreenReader }));
};
detectScreenReader();
}, []);
return (
<AccessibilityContext.Provider value={{ config, setConfig }}>
<div
className={clsx(
config.highContrast && 'high-contrast',
config.reducedMotion && 'reduced-motion',
`font-size-${config.fontSize}`
)}
>
{children}
</div>
</AccessibilityContext.Provider>
);
};
Semantic HTML and ARIA Implementation
Accessible Form Components:
interface AccessibleFormFieldProps {
id: string;
label: string;
error?: string;
helpText?: string;
required?: boolean;
children: React.ReactNode;
}
const AccessibleFormField: React.FC<AccessibleFormFieldProps> = ({
id,
label,
error,
helpText,
required,
children
}) => {
const helpTextId = helpText ? `${id}-help` : undefined;
const errorId = error ? `${id}-error` : undefined;
const describedBy = [helpTextId, errorId].filter(Boolean).join(' ');
return (
<div className="form-field">
<label
htmlFor={id}
className={clsx('form-label', required && 'required')}
>
{label}
{required && (
<span aria-label="required" className="required-indicator">
*
</span>
)}
</label>
{React.cloneElement(children as React.ReactElement, {
id,
'aria-describedby': describedBy || undefined,
'aria-invalid': error ? 'true' : 'false',
'aria-required': required || undefined
})}
{helpText && (
<div id={helpTextId} className="help-text">
{helpText}
</div>
)}
{error && (
<div
id={errorId}
className="error-text"
role="alert"
aria-live="polite"
>
{error}
</div>
)}
</div>
);
};
Accessible Data Table:
interface AccessibleTableProps<T> {
data: T[];
columns: TableColumn<T>[];
caption: string;
sortable?: boolean;
selectable?: boolean;
}
const AccessibleTable = <T extends Record<string, any>>({
data,
columns,
caption,
sortable = false,
selectable = false
}: AccessibleTableProps<T>) => {
const [sortConfig, setSortConfig] = useState<{ key: string; direction: 'asc' | 'desc' } | null>(null);
const [selectedRows, setSelectedRows] = useState<Set<string>>(new Set());
return (
<table
role="table"
aria-label={caption}
className="accessible-table"
>
<caption className="sr-only">{caption}</caption>
<thead>
<tr role="row">
{selectable && (
<th scope="col" role="columnheader">
<input
type="checkbox"
aria-label="Select all rows"
onChange={(e) => {
if (e.target.checked) {
setSelectedRows(new Set(data.map((_, index) => index.toString())));
} else {
setSelectedRows(new Set());
}
}}
checked={selectedRows.size === data.length && data.length > 0}
/>
</th>
)}
{columns.map((column) => (
<th
key={column.key}
scope="col"
role="columnheader"
aria-sort={
sortConfig?.key === column.key
? sortConfig.direction === 'asc' ? 'ascending' : 'descending'
: sortable ? 'none' : undefined
}
>
{sortable ? (
<button
onClick={() => handleSort(column.key)}
className="sort-button"
aria-label={`Sort by ${column.header}`}
>
{column.header}
</button>
) : (
column.header
)}
</th>
))}
</tr>
</thead>
<tbody>
{data.map((row, index) => (
<tr key={index} role="row">
{selectable && (
<td role="gridcell">
<input
type="checkbox"
aria-label={`Select row ${index + 1}`}
checked={selectedRows.has(index.toString())}
onChange={(e) => {
const newSelected = new Set(selectedRows);
if (e.target.checked) {
newSelected.add(index.toString());
} else {
newSelected.delete(index.toString());
}
setSelectedRows(newSelected);
}}
/>
</td>
)}
{columns.map((column) => (
<td key={column.key} role="gridcell">
{column.render ? column.render(row[column.key], row) : row[column.key]}
</td>
))}
</tr>
))}
</tbody>
</table>
);
};
Keyboard Navigation Standards
// Global keyboard navigation handler
const useKeyboardNavigation = () => {
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
// Skip navigation for form inputs
if (['INPUT', 'TEXTAREA', 'SELECT'].includes((event.target as Element).tagName)) {
return;
}
switch (event.key) {
case 'Tab':
// Let browser handle tab navigation
break;
case 'Escape':
// Close modals, dropdowns, etc.
closeAllPopovers();
break;
case 'Enter':
case ' ':
// Activate focused interactive elements
if (event.target instanceof HTMLElement &&
event.target.getAttribute('role') === 'button') {
event.preventDefault();
event.target.click();
}
break;
case 'ArrowDown':
case 'ArrowUp':
// Handle vertical navigation in menus
handleVerticalNavigation(event);
break;
case 'Home':
case 'End':
// Navigate to first/last element
handleHomeEndNavigation(event);
break;
}
};
document.addEventListener('keydown', handleKeyDown);
return () => document.removeEventListener('keydown', handleKeyDown);
}, []);
};
Architect Validation Report
Executive Summary
Overall Architecture Readiness: HIGH
Project Type: Full-stack application with React frontend and Java/Spring Boot microservices backend
Critical Strengths:
- Comprehensive DDD-based microservices architecture aligned with business domains
- Clear separation of concerns with well-defined bounded contexts
- Detailed API specifications with role-based security model
- Robust AWS cloud infrastructure with Swiss compliance considerations
- Comprehensive deployment and CI/CD strategy
Key Risks Identified:
- Complex multi-repository structure may increase coordination overhead
- Heavy reliance on external systems (AWS, Hostpoint DNS) creates potential failure points
- Advanced features (AI/ML recommendations) lack detailed implementation guidance
Section Analysis Results
| Section | Pass Rate | Status | Notes |
|---|---|---|---|
| Requirements Alignment | 95% | β PASS | All functional requirements covered, minor gaps in edge case handling |
| Architecture Fundamentals | 90% | β PASS | Strong DDD approach, clear component boundaries |
| Technical Stack & Decisions | 85% | β PASS | Justified technology choices, specific versions defined |
| Frontend Design & Implementation | 80% | β οΈ WARN | Good coverage, needs more component specification details |
| Resilience & Operational Readiness | 75% | β οΈ WARN | Monitoring strategy needs expansion |
| Security & Compliance | 85% | β PASS | Strong security model, GDPR compliance addressed |
| Implementation Guidance | 80% | β PASS | Good coding standards, testing strategy well-defined |
| Dependency Management | 90% | β PASS | Clear dependency mapping, versioning strategy |
| AI Agent Implementation Suitability | 85% | β PASS | Well-structured for AI implementation |
| Accessibility Implementation | 70% | β οΈ WARN | Basic coverage, needs more detailed guidance |
Risk Assessment
High Priority Risks:
-
Multi-Repository Coordination Complexity
- Risk: Development team coordination overhead, integration challenges
- Mitigation: Implement shared CI/CD pipelines, clear interface contracts
- Timeline Impact: +2-3 weeks for proper tooling setup
-
External DNS Provider Integration
- Risk: Complex Route53 alternative with Hostpoint, potential deployment issues
- Mitigation: Early proof-of-concept for DNS automation, fallback manual processes
- Timeline Impact: +1 week for DNS integration testing
-
Performance Under Load
- Risk: Multiple microservices may create latency chains
- Mitigation: Implement circuit breakers, comprehensive load testing
- Timeline Impact: +2 weeks for performance optimization
Medium Priority Risks:
-
AI/ML Feature Implementation
- Risk: Vague specifications for intelligent recommendations and search
- Mitigation: Phase 2 implementation with detailed research phase
- Timeline Impact: No immediate impact (deferred feature)
-
Swiss Compliance Complexity
- Risk: GDPR and Swiss data protection requirements may be underestimated
- Mitigation: Legal review of data handling, documented compliance procedures
- Timeline Impact: +1 week for compliance verification
Recommendations
Must-Fix Before Development:
- Expand monitoring and observability section with specific tools and metrics
- Add detailed component specifications for frontend architecture
- Create proof-of-concept for Hostpoint DNS integration
- Define specific performance benchmarks and SLAs
Should-Fix for Better Quality:
- Add more comprehensive accessibility implementation guidelines
- Expand error handling patterns with specific code examples
- Define detailed AI/ML architecture for future phases
- Add visual architecture diagrams using C4 model
Nice-to-Have Improvements:
- Consider adding GraphQL layer for flexible frontend data fetching
- Evaluate event sourcing for audit trail requirements
- Research serverless options for cost optimization
- Add chaos engineering practices for resilience testing
AI Implementation Readiness Assessment
Readiness Level: HIGH
Strengths for AI Implementation:
- Clear bounded context separation allows focused AI agent work
- Well-defined interfaces and API contracts
- Consistent naming conventions and patterns
- Comprehensive testing strategy provides safety net
Areas Needing Additional Clarification:
- Component-specific implementation patterns need more examples
- Complex business logic workflows need step-by-step breakdowns
- Integration testing scenarios require more detail
Complexity Hotspots to Address:
- Speaker workflow state machine implementation
- Multi-role permission enforcement
- Progressive publishing engine logic
- Real-time notification system
Final Validation Summary
The BATbern Event Management Platform architecture demonstrates HIGH readiness for implementation. The DDD-based microservices approach is well-suited for the complex event management domain, and the chosen technology stack is appropriate for the scale and requirements.
The architecture successfully addresses all major functional requirements from the PRD and provides a solid foundation for the revolutionary transformation from static website to dynamic event management platform.
Recommendation: PROCEED WITH DEVELOPMENT with attention to the identified must-fix items during Sprint 0 setup phase.