Use one public-content and one private-content bucket per site. All six buckets stay private at the S3 level. “Public” describes the audience reached through CloudFront, not anonymous access to the bucket.
Buckets and content prefixes
Mermaid source
flowchart TB S3["S3 — WSAI Production<br/>Six separate proposed buckets<br/>Block Public Access on ALL buckets"] S3 --> ORG S3 --> AI S3 --> IT subgraph ORG["WickedSpooky.org — logical site grouping"] OP["wsai-prod-org-public-UNIQUE<br/>index.html / assets/"] OX["wsai-prod-org-private-UNIQUE<br/>workbenches/ / documentation/<br/>architecture/ / training/<br/>runbooks/ / evidence/"] end subgraph AI["WickedSpooky.AI — logical site grouping"] AP["wsai-prod-ai-public-UNIQUE<br/>index.html / concepts/ / assets/"] AX["wsai-prod-ai-private-UNIQUE<br/>tenants/TENANT-ID/documents/<br/>tenants/TENANT-ID/downloads/<br/>Reserved for future features"] end subgraph IT["WickedSpookyIT.com — logical site grouping"] IP["wsai-prod-it-public-UNIQUE<br/>index.html / services/ / assets/"] IX["wsai-prod-it-private-UNIQUE<br/>tenants/TENANT-ID/documents/<br/>tenants/TENANT-ID/downloads/<br/>Reserved for future features"] end
Delivery and authorization
Mermaid source
flowchart TB ANON["Anonymous browser"] --> CF["CloudFront + HTTPS<br/>Appropriate site distribution"] CF -->|"OAC signed origin requests<br/>Distribution-scoped bucket policy"| PUB["Public-content buckets<br/>ORG / AI / IT<br/>S3 REST origins; Block Public Access ON"] USER["Website user including Sam<br/>Google sign-in; no browser AWS credentials"] --> AUTH["Proposed Cognito + ALB authentication"] AUTH --> APP["EC2 application authorization<br/>Approved membership + role<br/>Tenant and object checks; deny by default"] APP -->|"Scoped workload IAM role<br/>Authorized object reads"| PRIV["Private-content buckets<br/>ORG / AI / IT<br/>Block Public Access ON"] PRIV -->|"Object returned to application"| APP APP -->|"Authorized response<br/>No shared private-content cache"| USER RULE["No anonymous CloudFront behavior for private buckets<br/>No direct public S3 access<br/>Prefix names alone do not enforce permission"] RULE -.- PRIV
How to read this layout
- The domain headings are logical groupings, not nested buckets. Each named box is a separate bucket. UNIQUE is a placeholder for an available bucket-name suffix.
- Paths inside a bucket are object-key prefixes, not security boundaries. The AI/IT tenant prefixes reserve space for future features; they do not imply an implemented customer portal.
- The public bucket contains only approved public pages and artwork. All Ops architecture files—including HTML, Mermaid, SVG, JSON and downloads—belong in the private bucket at deployment.
- The prototype remains accessible for testing. Its local public/ directory is not a production content-classification rule.
Deployment controls
- Enable Block Public Access and bucket-owner-enforced ownership on every bucket; retain default encryption.
- Use a regular S3 REST origin with CloudFront OAC for public content. Allow only the designated distribution to read that site's public-content bucket. Do not use the S3 website endpoint for OAC.
- Route internal requests to the authenticated EC2 application. It checks identity, application group, tenant where applicable, and requested object before reading private S3 content with a scoped workload role.
- Keep private buckets out of anonymous CloudFront origin behaviors. Prevent direct origin bypass and shared caching of private responses.
- Test public delivery, denied anonymous S3 access, wrong-role denial and cross-tenant denial before publishing non-public data.
Identity separation
admin@wickedspooky.org is the AWS root identity. sam@wickedspooky.org is the day-to-day AWS administrator through the proposed Identity Center path and also has a separate Google website identity. Google application sessions receive no AWS credentials and inherit no AWS administrative permission. Backend IAM roles access S3 on behalf of authorized requests.
Small-business cost trade-off
Separate general-purpose buckets improve policy clarity without six always-on server charges. Costs depend on storage, requests, transfer and enabled features. Create private AI/IT buckets when their features need them; avoid unnecessary duplicate objects, retained versions and logs. A single bucket with scoped public/private prefixes can also be secured, but broader policies are easier to misconfigure. The proposed layout favors an easier-to-audit boundary.
AWS references
Prefixes · Origin access control · Block Public Access · S3 pricing
Rendering storage diagrams…