Skip to main content

Edge Function Upload Architecture (V2)

As of January 2026, Sable uses Edge Functions as the single source of truth for file uploads. This replaces the complex Bronze→Silver→Gold dbt pipeline with direct database inserts.

Why Edge Functions?

The original dbt pipeline had too many moving parts:

  • 6-step flow (upload → detection → bronze → job → dbt → verification)
  • Multiple polling services (file_watcher, worker)
  • Latency: minutes from upload to visible data
  • Debugging difficulty: failures could occur at any step

New architecture benefits:

  • Single endpoint handles everything
  • Sub-second upload to database
  • Both CLI and browser use same code path
  • Easy to debug (one place to look)

Architecture

┌─────────────────────────────────────────────────────────────────┐
│ UPLOAD PATH (V2) │
│ │
│ CLI (sable.js) ──┐ │
│ ├──→ Edge Function ──→ sable.* tables │
│ Browser Upload ──┘ + file_upload record │
│ │
└─────────────────────────────────────────────────────────────────┘

Edge Functions

sable_btig_upload_v2

Endpoint: https://hrezmztjmvzzibfhvpyo.supabase.co/functions/v1/sable_btig_upload_v2

Handles:

  • PnL files → sable.pnl_daily_v2
  • Perf files → sable.perf_daily_v2
  • Evidence PDFs → Supabase Storage

Features:

  • CSV parsing with type conversion
  • Account code → entity_id lookup via sable.account
  • SHA-256 deduplication
  • PDF reconciliation (CSV totals vs PDF totals)
  • Soft delete before insert (fn_upload_pnl_v2, fn_upload_perf_v2)
  • file_upload record for audit trail

sable_trans_upload_v2

Endpoint: https://hrezmztjmvzzibfhvpyo.supabase.co/functions/v1/sable_trans_upload_v2

Handles:

  • Transaction files → sable.trans_daily_v2

Features:

  • CSV parsing (handles MM/DD/YYYY dates, accounting numbers)
  • Account code extraction from filename
  • SHA-256 deduplication
  • Empty file tracking (tracked for audit, not rejected)
  • Soft delete before insert (fn_upload_trans_v2)
  • Batch insert (500 rows per batch)

Upload Flow

1. Client sends multipart/form-data
- file: CSV content
- org_id: Organization UUID
- force: "true" to bypass deduplication

2. Edge Function processes:
a. Parse CSV content
b. Compute SHA-256 hash
c. Check for duplicate (unless force)
d. Soft delete existing records for date range
e. Insert new records in batches
f. Create file_upload record

3. Response includes:
- rows_inserted
- rows_soft_deleted
- account_code
- date_range
- file_hash
- file_upload_id

CLI Usage

# Full pipeline (download + upload)
sable uploads pipeline --dates 2026-01-14

# Skip download, force re-upload
sable uploads pipeline --skip-download --force

# Single file upload
sable upload pnl /path/to/pnl_file.csv

Authentication

Edge Functions use service role key for database access:

// CLI (sable.js)
headers: {
'Authorization': `Bearer ${process.env.SUPABASE_SERVICE_ROLE}`
}

Soft Delete Pattern

Before inserting new data, existing records for the same date range are soft-deleted:

-- fn_upload_trans_v2(p_account_code, p_start_date, p_end_date)
UPDATE sable.trans_daily_v2
SET deleted_at = NOW()
WHERE account_code = p_account_code
AND trade_date BETWEEN p_start_date AND p_end_date
AND deleted_at IS NULL;

This allows:

  • Re-uploading corrected data
  • Auditing what was replaced
  • Recovery if needed

Deduplication

Files are tracked by SHA-256 hash:

SELECT id, filename, created_at
FROM sable.file_upload
WHERE file_hash = $1
AND org_id = $2
AND deleted_at IS NULL;

Use force=true to bypass and re-upload anyway.

Empty File Handling

Empty CSV files (header only, no data rows) are tracked but not rejected:

{
"success": true,
"skipped": true,
"reason": "empty_file",
"message": "File contains no data rows - tracked for audit"
}

This maintains audit trail of what was downloaded from BTIG.

Tables

TablePurpose
sable.pnl_daily_v2Daily P&L by position
sable.perf_daily_v2Daily returns (Modified Dietz)
sable.trans_daily_v2Transaction history (MTD)
sable.file_uploadUpload audit trail

Comparison: V1 dbt vs V2 Edge Functions

AspectV1 (dbt)V2 (Edge Functions)
LatencyMinutesSub-second
Code paths6+ services1 endpoint
DebuggingMultiple logsSingle function
ComplexityHighLow
Browser supportVia VercelDirect to Edge Function

Version History

  • 3.0.0-edge (2026-01-14): Trans uploads via Edge Function
  • 2.0.0 (2026-01): Initial Edge Function architecture