Data handling & PII
What user data lives in the marketplace DB
| Data | Table | Purpose |
|---|---|---|
| Email, name, image | user | Auth + display |
| Hashed password | user (via better-auth) | Auth — bcrypt cost ≥ 12 |
| Session tokens | session | HttpOnly cookies, no client access |
| OAuth refresh tokens | account (better-auth) | Cross-agent SSO |
| Subscription metadata | user_agent_subscriptions | Billing |
| Reviews | reviews | Public on agent detail page |
| Waitlist email | agent_waitlist | Marketing |
| Interaction emails | interactions | Marketing notification fan-out |
| Audit log (PostHog) | external | Request metadata, no bodies |
The marketplace does not store:
- Card numbers (Stripe holds them).
- Conversation transcripts (the agent service holds them).
- Agent-side runtime data (KBs, files, sessions).
What's in the audit log
AuditLogMiddleware records request metadata only:
requestId,userId,userRole,orgIdmethod,path,statusCode,errorCode,duration_msuserAgent,ip
It does not record request bodies, response bodies, or query parameters. That's intentional — query params can carry sensitive data (?token=...), so logging them is risky.
If you add a new event source (e.g. business-level events), filter PII before logging.
Cookie hygiene
| Property | Value |
|---|---|
HttpOnly | true (always) |
Secure | true in production |
SameSite | Lax in dev, None cross-origin in prod |
Path | / |
Max-Age | 30 days (better-auth default; configurable) |
CORS
app.enableCors({
origin: config.frontendUrl.split(',').map(s => s.trim()),
credentials: true,
});
Explicit allow-list. No wildcards. credentials: true is required for cookie auth.
Deletion / right to be forgotten
Currently no built-in user-deletion endpoint. To honor a deletion request:
BEGIN;
DELETE FROM "session" WHERE user_id = '<id>';
DELETE FROM "account" WHERE user_id = '<id>';
UPDATE reviews SET user_id = NULL WHERE user_id = '<id>';
DELETE FROM user_agent_subscriptions WHERE user_id = '<id>';
DELETE FROM "user" WHERE id = '<id>';
COMMIT;
Cancel any active Stripe subscriptions separately:
stripe subscriptions list --customer <cus_...> | jq -r '.[].id' | xargs -I{} stripe subscriptions cancel {}
Build this into a self-service flow when GDPR / DPDP / CCPA pressure demands it. Until then, manual.
Encryption
- In transit: TLS 1.2+ between every hop (browser ↔ ALB, ALB ↔ task, task ↔ RDS, task ↔ Stripe, task ↔ PostHog).
- At rest: RDS storage encryption on; Secrets Manager KMS-encrypted; S3 buckets server-side encrypted.
Backup & retention
| Data | Retention |
|---|---|
| RDS PITR | 7 days |
| RDS snapshots | 30 days |
| CloudWatch logs | 30 days (set explicitly; default is forever) |
| PostHog events | Project default (configurable per project) |
| Stripe events | indefinite (Stripe side) |
Adjust to match your compliance posture.
What agents are responsible for
Each agent service owns its own runtime data (knowledge bases, conversations, files). The marketplace explicitly does not see this. Each agent must publish its own privacy policy and handle its own data deletion.