medium priority medium complexity backend pending backend specialist Tier 1

Acceptance Criteria

Organisation configuration table (or equivalent feature_flags table) has a bufdir_submission_mode column accepting values 'file_based' | 'direct_api' with a CHECK constraint
Default value for all existing organisations is 'file_based' — no breaking change to current behaviour
BufdirExportService reads bufdir_submission_mode from the organisation config at submission time (not cached for more than 5 minutes to allow hot-toggle)
When submission_mode is 'file_based', the existing CSV/file generation path is invoked without modification
When submission_mode is 'direct_api', BufdirApiClient.submit() is called with the canonical payload
The orchestration layer (BLoC/Cubit and screen) does not need to know which mode is active — routing is fully encapsulated in BufdirExportService
Admin Supabase Edge Function or SQL script allows toggling submission_mode per organisation_id with an audit log entry
If bufdir_submission_mode is missing or null for an org, the service defaults to 'file_based' safely
Toggling from 'direct_api' back to 'file_based' is supported without data migration
Unit test confirms that BufdirExportService calls the correct submission path for each mode value

Technical Requirements

frameworks
Flutter
Dart
BLoC
Riverpod
apis
Supabase PostgreSQL 15
Supabase Edge Functions (Deno)
data models
bufdir_column_schema
bufdir_export_audit_log
performance requirements
Feature flag read adds no more than 5ms to the submission flow (cached with 5-minute TTL)
security requirements
Only super-admins or Norse Digital Products backend scripts can toggle submission_mode — not individual org coordinators
RLS policy restricts submission_mode writes to service role only
Audit log entry created on each toggle recording who changed the flag, from what value, to what value, and when
Organisation credential isolation: toggling one org's mode cannot affect another org

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Integration Task

Handles integration between different epics or system components. Requires coordination across multiple development streams.

Implementation Notes

Add `bufdir_submission_mode TEXT NOT NULL DEFAULT 'file_based' CHECK (bufdir_submission_mode IN ('file_based', 'direct_api'))` to the organisations (or org_config) table via a migration. In BufdirExportService, fetch the flag via a lightweight repository method `getSubmissionMode(orgId)` that caches with a 5-minute TTL using a simple in-memory map. Use a BufdirSubmissionMode enum (`fileBased`, `directApi`) mapped from the string value — never compare raw strings. The switch/routing logic should be a single `switch (mode)` expression in `BufdirExportService.submit()`.

Provide an admin Supabase Edge Function `admin/set-bufdir-submission-mode` accepting `{org_id, mode}` that validates the caller has super-admin role from JWT claims before writing.

Testing Requirements

Unit tests for BufdirExportService using mocked BufdirApiClient and mocked file-generation service: (1) file_based mode calls file path and does not call API client, (2) direct_api mode calls API client and does not call file path, (3) null/missing flag defaults to file_based, (4) 'direct_api' → 'file_based' toggle mid-session respects updated value after cache expiry. Integration test verifying CHECK constraint rejects invalid values. No UI tests required for this task.

Component
Bufdir API Client
service high
Epic Risks (2)
medium impact high prob dependency

Norse Digital Products has not yet completed API negotiations with Bufdir. If negotiations stall or Bufdir's API design diverges significantly from expectations, the API client may need substantial rework, or the epic may be blocked indefinitely.

Mitigation & Contingency

Mitigation: Implement the client against a locally defined stub of the expected Bufdir API schema. Isolate all Bufdir-specific schema mapping in a single adapter class so that changes to the actual API schema require changes in only one place. Keep the epic in 'interface-ready' status until real API credentials are available for integration testing.

Contingency: If API negotiations are not completed within the planned window, defer this epic without impact on any other epic — the PDF/CSV fallback path from Epics 1–4 delivers full standalone value. Mark the epic as blocked and resurface when negotiations conclude.

high impact low prob security

Bufdir API credentials stored in the application or edge function environment could be exposed through misconfigured secrets management, log leakage, or a compromised deployment pipeline, allowing unauthorised Bufdir submissions on behalf of the organisation.

Mitigation & Contingency

Mitigation: Store all Bufdir API credentials exclusively in Supabase Vault (or the integration credential vault component), never in client-side code or environment variables accessible to the Flutter app. Transmit credentials only from within the edge function, not from the Flutter client. Implement credential rotation support from the outset.

Contingency: If a credential leak is detected, immediately revoke and rotate the affected API credentials through Bufdir's credential management portal, audit submission logs for any unauthorised calls, and notify Bufdir's technical contact per the API agreement's security incident clause.