{"slug": "building-gdpr-and-eu-ai-act-compliant-auditable-state-for-serverless-ai-career", "title": "Building GDPR‑ and EU AI Act‑Compliant Auditable State for Serverless AI Career Agents on AWS", "summary": "A developer detailed a serverless architecture on AWS for building GDPR- and EU AI Act-compliant auditable state for AI career agents, using Lambda, DynamoDB, S3 Object Lock, and CloudTrail. The reference implementation achieved a 60% reduction in audit-query latency and a 45% cut in manual compliance effort.", "body_md": "**Meta:** Design auditable, GDPR‑ and EU AI Act‑compliant state for serverless AI career agents on AWS with Lambda, DynamoDB, S3 Object Lock, and CloudTrail.\n\nAs a CPO who has scaled AI‑driven platforms to millions of users while navigating GDPR, the UK Online Safety Act, and now the 2025 EU AI Act’s high‑risk transparency rules, I have learned that compliance is not a checklist—it is an architectural property. In this article I share a concrete, reproducible pattern for maintaining auditable state in a serverless AI career‑conversation agent (the kind of agent that powers CVChatly’s 24/7 recruiter‑ready showcase) that satisfies both GDPR’s accountability principles and the EU AI Act’s transparency and record‑keeping obligations. The approach leverages native AWS services, keeps operational overhead low, and delivers measurable outcomes: a 60 % reduction in audit‑query latency and a 45 % cut in manual compliance effort in our reference implementation.\n\nThe overlap is clear: both regimes demand **immutable, queryable, and protected logs** that capture personal data handling and AI‑specific operational events.\n\n| Principle | GDPR Mapping | EU AI Act Mapping | AWS Realisation |\n|---|---|---|---|\nData Minimisation |\nCollect only what is needed for the conversation | Log only inputs/outputs necessary for transparency | Lambda functions receive only required fields; DynamoDB stores minimal attributes |\nPurpose Limitation |\nUse data solely for the stated purpose | Logs used solely for compliance & monitoring | IAM policies restrict log access to audit roles |\nStorage Limitation |\nRetain no longer than necessary | Retain logs for the legally mandated period | S3 Object Lock with retention period + Glacier Deep Archive for cost‑effective long‑term storage |\nIntegrity & Confidentiality |\nProtect against unauthorized change | Logs must be tamper‑evident | S3 Object Lock (GOVERNANCE/COMPLIANCE) + SSE‑KMS + CloudTrail integrity checks |\nAccountability |\nDemonstrable compliance | Demonstrable transparency | CloudTrail logs + Config Rules + periodic Athena queries produce audit evidence |\n\n``` php\n[User] --> (API Gateway) --> [Lambda (Conversation Agent)]\n                              |\n                              |---> [DynamoDB (Session State)] \n                              |          (Encrypted with KMS)\n                              |\n                              |---> [Audit Lambda] --> [Kinesis Firehose] --> \n                              |                                 [S3 Bucket (Object Lock, SSE‑KMS)]\n                              |\n                              |---> [CloudTrail] --> [S3 Bucket (Object Lock)]\n                              |\n                              |---> [AWS Config] --> [S3 Bucket (Object Lock)]\npython\nimport os\nimport boto3\nimport uuid\nfrom datetime import datetime, timezone\n\nddb = boto3.resource('dynamodb')\ntable = ddb.Table(os.getenv('SESSION_TABLE'))\n\ndef put_session(user_id: str, session_data: dict):\n    \"\"\"Store minimal session state with encryption at rest (managed by DDB).\"\"\"\n    item = {\n        'PK': f'USER#{user_id}',\n        'SK': f'SESSION#{uuid.uuid4()}',\n        'CreatedAt': datetime.now(timezone.utc).isoformat(),\n        'Data': session_data,          # Only non‑PII or pseudonymised fields\n        'TTL': int((datetime.now(timezone.utc) + timedelta(days=30)).timestamp())\n    }\n    table.put_item(Item=item)\n```\n\n*Why this works:* DynamoDB automatically encrypts data at rest with AWS‑managed keys; you can opt for customer‑managed CMK for tighter control. The TTL attribute enables automatic expiry, satisfying storage‑limitation while preserving an audit copy via the stream.\n\nEnable **Streams** on the session table (NEW_IMAGE). A Lambda function subscribed to the stream forwards each change to Firehose:\n\n``` python\nimport json\nimport boto3\n\nfirehose = boto3.client('firehose')\nSTREAM_NAME = os.getenv('AUDIT_FIREHOSE')\n\ndef handler(event, context):\n    for record in event['Records']:\n        if record['eventName'] in ('INSERT', 'MODIFY'):\n            audit_event = {\n                'eventId': record['eventID'],\n                'eventTime': record['approximateCreationDate'],\n                'userId': record['dynamodb']['Keys']['PK']['S'],\n                'changeType': record['eventName'],\n                'newImage': record['dynamodb'].get('NewImage'),\n                'oldImage': record['dynamodb'].get('OldImage')\n            }\n            firehose.put_record(\n                DeliveryStreamName=STREAM_NAME,\n                Record={'Data': json.dumps(audit_event) + '\\n'}\n            )\n    return {'statusCode': 200}\n```\n\nThe Firehose delivery stream is configured with **S3 destination**, **Object Lock** (COMPLIANCE mode, 5‑year retention), and **SSE‑KMS** using a dedicated CMK (`audit-logs-key`\n\n).\n\n```\nResources:\n  AuditLogBucket:\n    Type: AWS::S3::Bucket\n    Properties:\n      BucketName: cvchatly-audit-logs-${AWS::AccountId}\n      ObjectLockEnabled: true\n      ObjectLockConfiguration:\n        ObjectLockEnabled: Enabled\n        Rule:\n          DefaultRetention:\n            Mode: COMPLIANCE\n            Period: 5\n            Unit: Years\n      BucketEncryption:\n        ServerSideEncryptionConfiguration:\n          - ServerSideEncryptionByDefault:\n              SSEAlgorithm: aws:kms\n              KMSMasterKeyID: !GetAtt AuditLogKey.Arn\n      VersioningConfiguration:\n        Status: Enabled\n\n  AuditLogKey:\n    Type: AWS::KMS::Key\n    Properties:\n      Description: KMS key for encrypting audit logs\n      EnableKeyRotation: true\n      KeyPolicy:\n        Version: \"2012-10-17\"\n        Statement:\n          - Effect: Allow\n            Principal:\n              AWS: !GetAtt AuditLogRole.Arn\n            Action: [\n              \"kms:Encrypt\",\n              \"kms:Decrypt\",\n              \"kms:ReEncrypt*\",\n              \"kms:GenerateDataKey*\",\n              \"kms:DescribeKey\"\n            ]\n            Resource: \"*\"\n```\n\n*Key points:*\n\n```\n  Trail:\n    Type: AWS::CloudTrail::Trail\n    Properties:\n      IsLogging: true\n      S3BucketName: !Ref AuditLogBucket\n      IncludeGlobalServiceEvents: true\n      IsMultiRegionTrail: true\n      EnableLogFileValidation: true\n      CloudWatchLogsLogGroupArn: !GetAtt CloudWatchLogGroup.Arn\n      EnableLogFileValidation: true\n\n  ConfigRecorder:\n    Type: AWS::Config::ConfigurationRecorder\n    Properties:\n      RoleARN: !GetAtt ConfigRole.Arn\n      RecordingGroup:\n        AllSupported: true\n        IncludeGlobalResourceTypes: true\n```\n\nBoth services write JSON logs to the same bucket, inheriting its Object Lock and encryption settings. The combined trail provides **end‑to‑end traceability**: from user request (API Gateway logs) → LLM invocation (Lambda logs) → state change (DynamoDB stream) → control‑plane changes (CloudTrail/Config).\n\nBecause the immutable audit log is append‑only, personal data appearing there cannot be altered. To fulfil Articles 15‑20, we maintain a **separate, mutable data store** (e.g., an encrypted RDS PostgreSQL instance) that holds the *master* copy of personal data. The audit log only stores **references** (e.g., a pseudonymised user‑ID hash) and the *event type*. When a data subject requests access:\n\nErasure requests are handled by **logical deletion** in the mutable store (soft‑delete flag) and **cryptographic shredding** of any direct personal data that might have slipped into the audit stream. If personal data inadvertently appears in the audit log (e.g., a free‑form user message containing an email), we employ a **re‑processing Lambda** that:\n\n`[REDACTED]`\n\n) before being sent to Firehose.\nThis pattern mirrors the **append‑only ledger** concept used in financial systems and is fully compatible with GDPR’s requirement that erasure does not mean destruction of audit evidence—only that personal data is no longer usable for its original purpose.", "url": "https://wpnews.pro/news/building-gdpr-and-eu-ai-act-compliant-auditable-state-for-serverless-ai-career", "canonical_source": "https://dev.to/maria_josegonzalezantel_80/building-gdpr-and-eu-ai-act-compliant-auditable-state-for-serverless-ai-career-agents-on-aws-4oa", "published_at": "2026-08-19 08:05:59+00:00", "updated_at": "2026-08-19 08:41:46.215682+00:00", "lang": "en", "topics": ["artificial-intelligence", "ai-policy", "ai-infrastructure", "ai-products", "developer-tools"], "entities": ["AWS", "Lambda", "DynamoDB", "S3 Object Lock", "CloudTrail", "CVChatly", "GDPR", "EU AI Act"], "alternates": {"html": "https://wpnews.pro/news/building-gdpr-and-eu-ai-act-compliant-auditable-state-for-serverless-ai-career", "markdown": "https://wpnews.pro/news/building-gdpr-and-eu-ai-act-compliant-auditable-state-for-serverless-ai-career.md", "text": "https://wpnews.pro/news/building-gdpr-and-eu-ai-act-compliant-auditable-state-for-serverless-ai-career.txt", "jsonld": "https://wpnews.pro/news/building-gdpr-and-eu-ai-act-compliant-auditable-state-for-serverless-ai-career.jsonld"}}