The architecture, step by step

A company sends confidential files to outside recipients by an expiring link, or asks an outsider to upload files to it. Recipients verify their email before they download. Every file is encrypted with KMS, scanned for malware and sensitive data before anyone can download it, and audit-logged. Click any box, step through any flow, and switch views with the tabs.

primary Tokyo ap-northeast-1DR Osaka ap-northeast-3, pilot lightapp FastAPI + Aurora PostgreSQLcompute EKS · Karpenter · KEDA · Argo CDdelivery Terraform · GitHub Actions OIDCdecisions 21 ADRs

Status: this is the design. The Terraform is written and statically checked, and nothing has been applied to a real AWS account yet. Numbers marked as budgets or estimates come from the design documents and haven't been measured. Read the Architecture doc · architecture.md on GitHub ↗

The whole system

One map of every part

Pick a path to see how a request moves, or click a box to see what it does, how it scales and what happens when it fails.

All connections at once. Choose a path to follow one kind of work, or click a box to see only its connections.

Edge · globalTokyo · ap-northeast-1 · 3 AZsEKS clusterscan node poolOsaka · ap-northeast-3 · pilot lightMember / admin → Route 53Recipient → Route 53Tenant automation → Route 53Route 53 → CloudFront + WAFCloudFront + WAF → S3 web bundleRoute 53 → Load balancerLoad balancer → API serviceMember / admin → Cognito: hosted sign-inMember / admin → S3 files: PUT parts with pre-signed URLsAPI service → PostgreSQLAPI service → S3 files: stream GetObjectAPI service → S3 large filesAPI service → Valkey: rate limitsAPI service → KMS + SecretsAPI service → S3 audit archive: grant intentAPI service → Egress proxy: code exchangeEgress proxy → CognitoOutbox relay → PostgreSQL: read outboxOutbox relay → SQS + DLQs: publishSQS + DLQs → Scan workerSQS + DLQs → Notification workerScan worker → S3 files: read + tagScan worker → ClamAV clamdScan worker → DLP inspectorScan worker → PostgreSQL: resultsScan worker → ValkeyGuardDuty → S3 large filesGuardDuty → SQS + DLQs: EventBridgeCognito → SQS + DLQs: sign-in eventsNotification worker → SES: emailNotification worker → PostgreSQLNotification worker → Egress proxyLifecycle jobs → PostgreSQLLifecycle jobs → S3 files: deleteLifecycle jobs → S3 large filesLifecycle jobs → S3 audit archive: archivePlatform operator → Operator APIOperator API → PostgreSQLOperator API → S3 audit archivePostgreSQL → Aurora replica: replicationS3 files → S3 replicas: RTCS3 large files → S3 replicasS3 audit archive → Audit replicaS3 web bundle → Web bundle copy: copyAPI service → Audit replica: security recordsCognito → Cognito standbyMember / adminbrowserRecipientor uploader, no accountTenant automationREST API, API keyPlatform operatorCLI, port-forwardRoute 53DNS, health checksCloudFront + WAFserves the web appCognitosign-in, MFA, SSOLoad balancerALB + WAFS3 web bundleReact appAPI serviceFastAPI · 3–15 podsEgress proxyEnvoy, system nodesNotification workeremails, codesOutbox relayDB → SQSScan workerClamAV clamdDLP inspectorLifecycle jobsexpiry, deletionOperator APIns sft-operatorPostgreSQLAurora · the recordSQS + DLQswork queuesValkeyrate-limit countersKMS + Secrets8 keys per envSESemailS3 filesup to 2 GBS3 large files2 to 5 GB filesGuardDutymalware, over 2 GBS3 audit archiveObject LockCognito standbyno usersWeb bundle copyCDN failoverEKS standby1 node, apps at 0SES Osakaapproved, idleAurora replicaglobal databaseS3 replicasboth bucket pairsAudit replica+ direct writes
People and clientsEdge and entryOur code on KubernetesIsolated scanning and keysData storeMessaging and emailOsaka standbyDashed line: browser uploads straight to S3

Details

Click any box on the map to see its responsibility, how it scales, what happens when it fails and its IAM permissions.

Six ideas the design rests on

If you remember nothing else

ADR-0002

One codebase, many entry points

The API, the outbox relay, every worker and the lifecycle jobs come from one container image with different start commands. They share the data model and service layer: one API service plus workers, no microservice sprawl.

ADR-0011 · 0013

PostgreSQL is the single source of truth

Every state change, its audit record and any “do this later” message are written in one transaction. The outbox relay forwards the messages to SQS, so a crash between the database write and the queue send loses nothing.

ADR-0012 · 0018

Uploads skip the API, downloads go through it

Browsers upload parts straight to S3 with pre-signed URLs. Downloads stream through the API under a session-bound grant, so the API can count downloads, check revocation every 30 s and stop a stream within 60 s.

ADR-0019 · 0022

Nothing is shareable until it's scanned

The API's IAM role can read an object only when its scan tag says clean. Only the scan worker can set that tag, after ClamAV (or GuardDuty for files over 2 GB) and the DLP inspector have seen the bytes.

ADR-0004 · 0005

Git is the desired state

Terraform owns AWS resources. Argo CD owns what runs in Kubernetes and keeps it equal to Git. Deploying, rolling back and fencing a failed region are all commits; nobody runs kubectl apply.

ADR-0006 · 0011

Tokyo serves, Osaka waits

Data replicates to Osaka all the time, and Osaka's network, cluster control plane and one system node stay up. A regional failover only has to add compute, inside a 1-hour budget.

Built from docs/architecture.md, ADR-0002 to ADR-0022, docs/design/capacity-and-cost.md, docs/security/encryption-and-keys.md, infra/ and .github/workflows/ in the public repository, as of 5 November 2026. The Markdown in the repository is the source of truth.