Surplus logo
Surplus Docsby Sharing Excess

Data protection

Surplus handles real personal data — the names, emails, phone numbers, and addresses of the people and organizations in the food-rescue network. This page describes how that data is protected.

What personal data exists

The main categories of personal or sensitive data are:

  • Account data — user name, email, and profile image.
  • Organization contacts — contact names, emails, phone numbers, and addresses for donors and recipients.
  • Location data — addresses and coordinates of donor and recipient locations.
  • Operational notes — free-text notes on events and routes.
  • Billing data — billing names, emails, and addresses tied to cost-sharing agreements. Payment processing itself is handled by Stripe; Surplus does not store card data.

Encryption in transit

All traffic in deployed environments runs over HTTPS. The API sets HSTS with a two-year max-age, includeSubDomains, and preload, so browsers refuse to connect over plain HTTP. The session cookie is marked Secure in staging and production, so it is never transmitted unencrypted. See Network and application security.

Encryption at rest

Surplus stores data in managed services that encrypt data at rest as a standard part of their platforms:

  • PostgreSQL — Neon.
  • Redis — Upstash.
  • File storage — Tigris (S3-compatible).

Backups are written to separate, private storage buckets. See Infrastructure and secrets and Backups and recovery.

Keeping PII out of logs

Observability is valuable, but logs are a classic place for personal data to leak. In staging and production, Apitally request logging is configured in apps/server/src/index.ts to mask sensitive fields before they're ever recorded:

  • Body fields masked include email, phone, address, contact name, latitude/longitude, name, notes, internal notes, additional emails, all billing fields, and one-time codes.
  • Headers masked include Cookie and Authorization, so session tokens never appear in logs.

Application logs are structured and enriched with a request id, user id, and route — identifiers for correlation — rather than raw personal data.

Data minimization on public surfaces

Where data is exposed publicly, it's minimized to only what's needed. The public map/GeoJSON surface, for example, exposes the geographic shape needed to render the map and omits identifying address detail. The principle: public endpoints return the minimum necessary, and nothing more.

File uploads

Uploaded files (attachments, logos, documents) are:

  • Size-limited — a 25 MB cap (MAX_UPLOAD_FILE_SIZE_BYTES), enforced on the server and surfaced in the UI.
  • Access-controlled — never public; served only through the authenticated API with per-entity ownership checks. See Authorization.
  • Sanitized — filenames are sanitized and path traversal is rejected.

Client error monitoring and PII

The web client uses Sentry for error monitoring in staging and production. It is configured with sendDefaultPii: false in apps/client/src/lib/sentry.ts, so Sentry does not automatically attach default request or user context (such as cookies or IP-derived user data) to error reports. Error events still include the release and environment tags needed for debugging. Sentry remains a sub-processor for error telemetry; see the sub-processor list.

What still needs to be documented

A few data-governance items are organizational policies rather than code, and are being formalized:

  • Data retention and deletion — how long each category of data is kept, and the process for deleting a user's or organization's data on request.
  • Data subject access requests — the operational process for fulfilling access/deletion requests.
  • Data-processing agreements — formal DPA status with each sub-processor listed under Infrastructure and secrets.

These are tracked as open items rather than documented here, to avoid implying a policy that isn't yet ratified.