critical priority high complexity infrastructure pending infrastructure specialist Tier 4

Acceptance Criteria

The Edge Function rejects any request without a valid Supabase JWT with HTTP 401 before any business logic executes
The authenticated user's role is verified to be 'coordinator' or 'org_admin'; any other role returns HTTP 403
The orgId in the request body is validated to match the orgId encoded in the JWT claims; mismatches return HTTP 403
Request body is validated: orgId (non-empty string), dateRangeStart and dateRangeEnd (ISO 8601 dates, start <= end), targetSystem (one of 'xledger' | 'dynamics') — invalid input returns HTTP 422 with field-level error details
AccountingCredentialsVault credentials are fetched inside the Edge Function and passed to AccountingExporterService; credentials are never logged or included in any response body
On DUPLICATE export result, the function returns HTTP 409 with a structured JSON body including the original export run ID
On successful export, the function returns HTTP 200 with { exportRunId, fileUrl, recordCount, status: 'completed' }
On pipeline failure, the function returns HTTP 500 with { status: 'failed', errorCode } — no stack traces or internal error messages in the response
The function is deployed and callable via the Supabase Functions endpoint; it does not require the service-role key from the Flutter client
Integration test for happy path: valid JWT + valid body → 200 with fileUrl
Integration test for double-export: second identical request → 409
Cold-start latency is under 2 seconds; total response time under 35 seconds for standard exports

Technical Requirements

frameworks
Supabase Edge Functions (Deno runtime)
Supabase Auth (JWT verification)
apis
Supabase Auth Admin API (JWT decode + role lookup)
AccountingCredentialsVault internal API
AccountingExporterService (internal invocation)
Supabase PostgREST (for any direct DB reads needed within the function)
data models
ExportRunResult
AccountingCredentials
OrgAccountingConfig
performance requirements
JWT validation must add no more than 100ms overhead
Function must not hold open Supabase connections longer than necessary; use connection pooling via Supabase client
Response payload must not exceed 10KB; file content is never returned inline
security requirements
Verify JWT signature using Supabase JWT secret — do not trust unverified claims
All secrets (vault credentials, API keys) must be accessed via Supabase Vault or environment variables set in the Supabase dashboard, never hardcoded
CORS policy must restrict origins to the app's known domains; wildcard '*' is not acceptable in production
Rate-limit export requests per org: maximum 10 export triggers per hour enforced at the Edge Function level
Input sanitisation: dateRangeStart/End parsed and re-serialised as ISO strings before passing downstream to prevent injection

Execution Context

Execution Tier
Tier 4

Tier 4 - 323 tasks

Can start after Tier 3 completes

Implementation Notes

Structure the Edge Function handler as: (1) parse and validate Authorization header, (2) decode and verify JWT, (3) extract role and orgId from claims, (4) validate request body with a typed schema (Zod or manual Deno validation), (5) fetch credentials from vault, (6) instantiate and invoke AccountingExporterService, (7) map ExportResult to HTTP response. Keep each step in its own function for testability. Use Deno's built-in crypto for JWT verification rather than an external library to minimise cold-start time. The Edge Function should import AccountingExporterService as a shared module (placed in supabase/functions/_shared/) to avoid duplicating pipeline logic.

Ensure the function's Deno permissions are scoped minimally (--allow-net for Supabase, --allow-env for secrets). Deploy with supabase functions deploy generate-export and document the required environment variables in a .env.example file committed to the repo (with placeholder values only).

Testing Requirements

Integration tests using Supabase local development stack (supabase start): (1) Happy path — mint a valid coordinator JWT, POST valid body, assert HTTP 200 and ExportRun row in DB. (2) Double-export — repeat identical request, assert HTTP 409 and no new ExportRun. (3) Auth failure — POST without Authorization header, assert HTTP 401. (4) Role failure — use a peer mentor JWT, assert HTTP 403.

(5) Org mismatch — use a coordinator JWT for org A but request orgId of org B, assert HTTP 403. (6) Invalid body — missing dateRangeStart, assert HTTP 422. (7) Vault credential retrieval failure — mock vault to throw, assert HTTP 500 with errorCode but no credential content. All tests must be automated and run in CI on every PR touching the Edge Function.

Component
Accounting Credentials Vault
infrastructure medium
Epic Risks (3)
high impact medium prob technical

Adding exported_at and export_run_id columns to expense_claims requires a live migration on a table shared with the approval workflow. A poorly timed migration could lock the table and block claim submissions or approvals.

Mitigation & Contingency

Mitigation: Use non-blocking ADD COLUMN with a DEFAULT of NULL (no backfill needed) executed during a low-traffic window. Test migration rollback on a staging replica before production deployment.

Contingency: If migration causes table lock contention, roll back and reschedule for a maintenance window. Use a feature flag to gate the export UI until the migration completes successfully.

medium impact high prob scope

Chart of accounts mapping configurations for Xledger and Dynamics may not be fully specified by stakeholders at development time, leaving the mapper with incomplete data and causing validation failures for unmapped expense categories.

Mitigation & Contingency

Mitigation: Implement the mapper to return a structured validation error (not a crash) for any unmapped field, and surface these errors clearly in the export confirmation dialog. Request full mapping tables from Blindeforbundet and HLF stakeholders as a pre-condition for this epic.

Contingency: If mappings arrive incomplete, ship the mapper with the available subset and mark unmapped categories as excluded (skipped with reason). Coordinators see which categories are skipped and can manually submit those records.

medium impact medium prob dependency

Supabase Vault configuration for storing per-org accounting credentials may require infra permissions or environment secrets not yet provisioned in staging or production, blocking development and testing of credential retrieval.

Mitigation & Contingency

Mitigation: Provision Vault secrets and environment configuration in staging as the first task of this epic. Document the exact secret naming convention and rotation procedure before implementation begins.

Contingency: If Vault is unavailable, use environment variables scoped to the Edge Function as a temporary fallback for development. Block production deployment until Vault-based storage is confirmed operational.