Create Supabase Storage bucket for receipts
epic-travel-expense-registration-foundation-task-005 — Provision the Supabase Storage bucket for receipt images with appropriate bucket policies: private access (no public URLs), org-level path prefixes (org_id/claim_id/receipt.jpg), RLS policies allowing claimants to upload their own receipts and coordinators to read receipts within their organisation. Configure CORS and file-size limits.
Acceptance Criteria
Technical Requirements
Implementation Notes
Define the bucket using Supabase CLI's `supabase/seed.sql` or a dedicated storage migration script so it can be reproduced in staging and production environments without manual Studio clicks. The path convention `{org_id}/{claim_id}/{filename}` is critical — the RLS policies rely on this structure using Supabase Storage's `name` column (which contains the full path) and string prefix matching. For the coordinator read policy, use a helper function `get_user_org_id()` that reads from a user_roles or org_members table to avoid hardcoding org_id in JWT claims. Document the signed URL expiry as a configurable constant (RECEIPT_SIGNED_URL_TTL_SECONDS = 3600) referenced from both the bucket config and the adapter in task-006.
Testing Requirements
Write Supabase Storage RLS tests using the Supabase test helpers or manual curl-based scripts: (1) Verify authenticated claimant can upload to their org/claim path; (2) Verify claimant cannot upload to a different org's path; (3) Verify coordinator can read all files under their org prefix; (4) Verify claimant from org A cannot read receipts from org B; (5) Verify file size limit rejects files over 5 MB; (6) Verify MIME type restriction rejects text/plain uploads. Run these as part of the infrastructure smoke test suite in CI.
Row-level security policies for expense claims must correctly scope data to organisation, role (peer mentor sees own claims only, coordinator sees org-wide queue), and claim status. Incorrect RLS can expose claims cross-organisation or prevent coordinators from accessing the attestation queue.
Mitigation & Contingency
Mitigation: Define RLS policies in code-reviewed migration files. Write integration tests that attempt cross-org reads with different JWT roles and assert access denial. Review with a second engineer before merging migrations.
Contingency: If RLS is misconfigured post-deployment, disable the affected policy temporarily and apply a hotfix migration within the same release window. No claim data is exposed publicly due to Supabase project-level auth requirement.
The auto-approval Edge Function is triggered server-side on expense insert. Cold-start latency or Edge Function failures can block the submission response and degrade UX, especially on mobile networks.
Mitigation & Contingency
Mitigation: Implement the auto-approval Edge Function client with a timeout and graceful fallback: if no result is received within 5 seconds, treat the claim as 'pending' and poll for the status update via Supabase Realtime. Keep the Edge Function warm with a periodic ping.
Contingency: If Edge Function reliability is unacceptable, move auto-approval evaluation to a database trigger or Postgres function as an interim measure, accepting that threshold configuration changes require a migration rather than a settings update.
The expense type catalogue and threshold configuration are cached locally for offline use. If an organisation updates their catalogue exclusion rules or thresholds while a peer mentor is offline, the local cache may allow submissions that violate the new policy.
Mitigation & Contingency
Mitigation: Cache entries include a TTL (24 hours). On connectivity restore, refresh cache before allowing new submissions. Server-side validation in the Edge Function and save functions provides a second enforcement layer.
Contingency: If a stale-cache submission passes client validation but fails server validation, surface a clear error message explaining that the expense type rules have been updated and prompt the user to review their selection with the refreshed catalogue.