Operations & Security

This document consolidates security implementation, performance standards, accessibility guidelines, and operational validation for the BATbern Event Management Platform.

Last updated: June 21, 2026 Reading time: 13 minutes File size: 23.38 KB

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 domain auth.batbern.ch (Story 12.9 β€” Amplify POSTs the OAuth code exchange to https://auth.batbern.ch/oauth2/token), CloudFront CDN (*.cloudfront.net), branded CDN domain (cdn.batbern.ch), and api.batbern.ch
    • frame-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: the auth.batbern.ch switch broke federated login in production because only the gateway CSP was considered β€” any new outbound origin must be added to the frontend-stack connect-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-origin to cross-origin for CDN asset delivery.
  • CDN SVG hardening (Story 12.12 review, finding #3): users can upload SVGs (profile pictures, logos) that cdn.batbern.ch serves as image/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’s ResponseHeadersPolicy (batbern-content-cache-*, infrastructure/lib/stacks/storage-stack.ts) therefore sends CSP sandbox (blocks script execution when the resource IS the document; <img> embedding of PNG/JPEG/WebP/SVG is unaffected) plus X-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 the Authorization header; ai_generation_log is hash-only.
  • Canonical mapping + status: docs/security/owasp-llm-agentic-checklist.md.
  • CI: advisory .github/workflows/ai-security.yml (aiSecurityTest @Tag subset + 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.ts CustomUserPoolDomain). Existing email/password accounts are linked transparently (AdminLinkProviderForUser in the PreSignUp_ExternalProvider trigger); brand-new Google users are JIT-provisioned as ATTENDEE and pass the ToS consent gate before reaching protected routes (see 06b-user-lifecycle-sync.md Patterns 1b/F/C). The login button is gated on the runtime features.sso flag served by GET /api/v1/config β€” kill-switch: set FEATURES_SSO_ENABLED=false on the API gateway and restart (no frontend rebuild). Deactivated accounts are blocked request-time by the gateway AccountActiveFilter (federation never fires PreAuthentication).
  • Unconfirmed sign-up recovery: a daily CognitoConfirmationResendJob re-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 granted cognito-idp:ListUsers + cognito-idp:ResendConfirmationCode, scoped to the user-pool ARN (the SPA client has no secret, so no SecretHash is 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:

yaml
# 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:

yaml
# 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:

yaml
# 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

typescript
// 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:

typescript
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:

typescript
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

typescript
// 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:

  1. 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
  2. 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
  3. 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:

  1. 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)
  2. 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:

  1. Expand monitoring and observability section with specific tools and metrics
  2. Add detailed component specifications for frontend architecture
  3. Create proof-of-concept for Hostpoint DNS integration
  4. Define specific performance benchmarks and SLAs

Should-Fix for Better Quality:

  1. Add more comprehensive accessibility implementation guidelines
  2. Expand error handling patterns with specific code examples
  3. Define detailed AI/ML architecture for future phases
  4. Add visual architecture diagrams using C4 model

Nice-to-Have Improvements:

  1. Consider adding GraphQL layer for flexible frontend data fetching
  2. Evaluate event sourcing for audit trail requirements
  3. Research serverless options for cost optimization
  4. 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:

  1. Component-specific implementation patterns need more examples
  2. Complex business logic workflows need step-by-step breakdowns
  3. 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.