critical priority low complexity infrastructure pending infrastructure specialist Tier 0

Acceptance Criteria

Bucket named 'bufdir-exports' exists in the Supabase project and is set to private (public = false)
Allowed MIME types are restricted to: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet (xlsx), text/csv, application/pdf — any other MIME type is rejected on upload
Maximum file size is configured (e.g. 50 MB per file — confirm exact limit with team based on expected export sizes)
A Storage RLS policy permits authenticated users to INSERT (upload) objects only under the path prefix matching their org_id JWT claim: /{org_id}/*
A Storage RLS policy permits authenticated users to SELECT (download) objects only under their own org_id path prefix
Bucket configuration is captured in a Supabase migration or Infrastructure-as-Code file (not only via the dashboard) so it can be reproduced in staging and production
Path convention document (inline in the migration file or a dedicated docs section) specifies: /{org_id}/{year}/{export_id}/{filename}.{ext} with field definitions
Smoke test: upload a test CSV file to a valid org path using a service-role client succeeds; upload to a different org's path using a non-service-role JWT is rejected
Smoke test: upload of an unsupported MIME type (e.g. application/json) is rejected

Technical Requirements

frameworks
Supabase Storage
Supabase migrations or seed scripts for bucket config
apis
Supabase Storage API (createBucket, updateBucket, uploadObject)
Supabase RLS on storage.objects table
data models
storage.objects (Supabase internal)
storage.buckets (Supabase internal)
performance requirements
Signed download URLs must be generated with a short TTL (e.g. 60 minutes) for security — document the TTL in path convention notes
Bucket should be provisioned in the same Supabase region as the database to minimise latency
security requirements
Bucket must be private — no public read access under any circumstances given the sensitive Bufdir reporting data
Path must include org_id as the first segment so RLS path-prefix matching is unambiguous
Service role must be used only in backend/orchestrator context — Flutter app client should use user-scoped JWTs for upload/download
Signed URLs preferred over direct bucket access for download links returned to users

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Supabase bucket configuration (allowed MIME types, file size limits) can be set either via the dashboard or programmatically using the Supabase Management API or a seed script. Prefer a seed/migration script for reproducibility across environments. RLS on storage.objects uses path-based policies — the policy expression should use (storage.foldername(name))[1] = auth.jwt()->>'org_id' or equivalent to match the first path segment. The year segment in the path (/{org_id}/{year}/{export_id}/) is derived from the export's period_start year at upload time — document this derivation rule explicitly.

Export filenames should include the org_id and period for human readability: e.g. nhf_2024_Q4_{export_id}.xlsx. Confirm the 50 MB limit is sufficient for the largest expected Bufdir export (HLF had ~380 individual activity records per year per person — multiply by expected number of peer mentors).

Testing Requirements

No automated flutter_test needed for bucket creation itself. Provide a manual smoke-test checklist: (1) create bucket via migration/script on local Supabase and verify it appears in storage dashboard; (2) upload xlsx file to valid path with service role — expect 200; (3) upload json file to valid path — expect 400/415; (4) upload to another org's path with a user JWT — expect 403; (5) list objects under own org path with user JWT — expect only own files. Include these steps in the task PR description so the reviewer can run them.

Component
Export File Storage Adapter
infrastructure low
Epic Risks (2)
high impact medium prob security

RLS policies for the audit log and schema config tables must correctly handle multi-chapter membership hierarchies (up to 1,400 local chapters for NHF). Incorrect policies could either over-expose data across organisations or prevent legitimate coordinator access, both of which are serious compliance failures.

Mitigation & Contingency

Mitigation: Design RLS policies using the existing org hierarchy resolver pattern. Write integration tests that verify cross-organisation isolation with representative fixture data covering NHF's multi-level hierarchy before merging.

Contingency: If RLS policies prove too complex to express safely in Postgres, implement a Supabase Edge Function as a data access proxy that enforces isolation in application code, with RLS serving as a secondary defence layer.

medium impact medium prob scope

Bufdir's column schema is expected to evolve as Norse Digital Products negotiates a simplified digital reporting format. If the schema config versioning model is too rigid, applying Bufdir schema updates without a code deployment could fail, forcing emergency releases.

Mitigation & Contingency

Mitigation: Design the schema config table to store the full JSON column mapping as a JSONB field with a version number. Provide an admin API to upsert new versions without any schema migration required.

Contingency: If the versioning model is insufficient for a Bufdir schema change, fall back to a code deployment with the updated default schema, using the database config only for org-specific overrides.