Static Asset Repository
The Static Asset Repository provides per-tenant storage and access control for customer assets (images, videos, documents, etc.). It is designed as a microservice encapsulating a single bounded context — loosely coupled from other components for independent development, deployment, and scaling.
Architecture
Section titled “Architecture”Three layers compose the repository:
| Layer | Technology | Responsibility |
|---|---|---|
| Access Control | API Gateway + Cognito | JWT authentication; redirect unauthenticated requests to login |
| API Layer | AWS Lambda | Tenant namespace partitioning, pre-signed URL generation |
| Storage Backend | S3 (single bucket, or multiple if needed) | Object storage, versioning, encryption |
Only the Lambda function has direct access to the S3 bucket. The bucket is invisible to API consumers.
Key Concepts
Section titled “Key Concepts”Three Key Types
Section titled “Three Key Types”| Key | Format | Purpose |
|---|---|---|
| Arda-Key | {owning-module}/{entity-type}/{property-name}/{asset-name}.{ext} | Human-readable identifier specified by the caller |
| System-Key | {uuid}/{basename from Arda-Key} | Unique identifier generated at creation; prevents name clashes |
| Storage-Key | {tenantId}/{System-Key} | Actual S3 object key; partitions storage by tenant |
The Arda-Key is stored as S3 object metadata (arda-key). The API maps dynamically between System-Key and Storage-Key.
Pre-Signed vs Persistent URLs
Section titled “Pre-Signed vs Persistent URLs”Pre-Signed URL — time-limited (initially 5-minute expiry), generated per request, provides direct S3 read or write access. Used for the actual upload/download to avoid routing large payloads through Lambda.
Persistent URL — stable URL of the form https://{api-gateway-host}/assets/{systemKey}. Stored in entity payloads to reference the asset long-term. Resolves to a pre-signed URL at read time via a 302 redirect.
API Endpoints
Section titled “API Endpoints”| Method | Path | Description |
|---|---|---|
POST | /assets | Register an asset; returns Persistent URL + pre-signed PUT URL |
GET | /assets/{systemKey} | 302 redirect to pre-signed GET URL |
HEAD | /assets/{systemKey} | Asset headers (content-type, content-length, checksum) |
GET | /versions/{systemKey} | List all versions of the asset |
All endpoints require a valid JWT token. The API Gateway handles authentication; the Lambda handles authorization (currently a no-op, planned for future implementation).
Write Flow
Section titled “Write Flow”- Client (SPA via BFF) calls
POST /assetswithardaKey,contentType, optionalcontentLengthandchecksum - Lambda generates System-Key and Storage-Key, creates a pre-signed PUT URL, returns
(persistentUrl, presignedUrl) - Client uploads file bytes directly to S3 using the pre-signed PUT URL
- Client persists the Persistent URL in the owning entity via the relevant Component’s API
- The Component calls
HEAD {persistentUrl}to verify the upload completed before persisting
Read Flow
Section titled “Read Flow”- Client retrieves the Persistent URL from the entity via the Component API
- Client calls
GET {persistentUrl}through API Gateway - Lambda resolves System-Key to Storage-Key, generates a pre-signed GET URL
- API returns
302 redirectto the pre-signed GET URL - Client fetches the asset directly from S3
Storage Configuration
Section titled “Storage Configuration”S3 Bucket:
- Block all public access (on by default)
- Server-side encryption: SSE-S3 (Amazon managed keys)
- Bucket Versioning: enabled (audit trail and rollback)
- Removal Policy:
RETAIN(bucket survives CDK stack deletion) autoDeleteObjects: false(objects preserved on bucket deletion)- Object ownership: bucket owner (ACLs disabled)
- Default tags:
Infrastructure,Partition,Component
S3 Object Metadata per Asset:
arda-user— JWTsubclaim of the creating userarda-key— Arda-Key of the asset (for logging and debugging)
IAM Policy for Lambda (least privilege):
s3:PutObject,s3:GetObject,s3:GetObjectVersion,s3:ListBucketVersions- No other IAM policies grant access to the bucket; bucket policy explicitly denies all other access
Lifecycle and Decommission
Section titled “Lifecycle and Decommission”| Event | Expected Behavior | Operator Action |
|---|---|---|
| Create | CloudFormation creates and configures the bucket | Verify tags and baseline controls |
| Update (no replacement) | In-place configuration update | Validate no permission regression |
| Update (replacement required) | New bucket created; old bucket retained | Plan data migration before cutover |
| Stack deletion | Stack deleted; bucket and versions retained | Record retained bucket in inventory |
| Final decommission | No automatic deletion | Follow decommission runbook |
Decommission runbook:
- Obtain legal/compliance approval
- Archive/export required data to long-term storage if mandated
- Remove all objects, versions, and delete markers
- Delete the bucket
- Close the decommission ticket and remove from retained-resources inventory
Use Case Cross-References
Section titled “Use Case Cross-References”The user-facing behavioral contracts that drive this infrastructure are defined in the product use case specifications. See Use Cases Analysis (not yet published) for the full traceability between use cases and design decisions.
| Use Case | Relevance to FileStore |
|---|---|
GEN::MEDIA::0001 | Set Entity Image — defines the user-facing upload and URL input flows that the Write Flow implements |
GEN::MEDIA::0002 | Remove Entity Image — clears the entity reference; superseded assets are retained for historical display (archival/purging is out of scope) |
Copyright: © Arda Systems 2025-2026, All rights reserved