---
id: WF-PFM-DJE-010
title: Upload a file in signed parts
trigger: 'A client begins, resumes, completes or abandons an input-file upload.'
product_area: platform
module: data-job-engine
revision: 1
status: draft
design_intent: confirmed
origin: >-
  services-platform:docs/proposals/rhythm-data-job-engine-plan.md §6.2 inputs
  and §16 source files (approved 2026-10-12);
  services-platform:docs/queued-work.md (no external call inside a transaction)
implementation_evidence: observed
inference: []
source_sha:
  services-platform: ad3f6a6582843008ed0f5a17b131878d15829af1
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
implements:
  - CAP-PFM-DJE-011
moves:
  - STATE-PFM-DJE-003
crosses:
  - SVC-PFM-DJE-data-jobs
hops:
  - 'n': 1
    actor_or_service: Upload route and session service (open)
    action: >-
      Validate the declaration, select part sizing, start multipart storage and
      save the open session.
    anchor: >-
      services-platform:modules/data-jobs/services/uploads/upload-sessions.ts:openUpload
    seam: sync
    on_failure: >-
      Refuse invalid declarations before storage; bucket abandonment cleans an
      unrecorded transfer.
    left_inconsistent: A crash can leave an unreachable multipart transfer.
    observer: The request response exposes the result.
    provenance: test
    source: >-
      services-platform:modules/data-jobs/tests/integration/services/uploads/upload-sessions.integration.spec.js#refuses
      a declaration before it creates anything in S3, so a refused request
      leaves no upload behind
  - 'n': 2
    actor_or_service: Upload route (parts)
    action: Sign up to 100 requested part addresses with one-hour validity.
    anchor: >-
      services-platform:modules/data-jobs/services/uploads/upload-sessions.ts:presignUploadParts
    seam: sync
    on_failure: Refuse settled sessions or invalid part requests.
    left_inconsistent: No session state is advanced.
    observer: The request response exposes the result.
    provenance: test
    source: >-
      services-platform:modules/data-jobs/tests/integration/services/uploads/upload-sessions.integration.spec.js#refuses
      more than a hundred parts at a time, which is the page the protocol
      presigns
  - 'n': 3
    actor_or_service: Client and storage
    action: >-
      Send parts to storage and read available parts when resuming an
      interruption.
    anchor: >-
      services-platform:modules/data-jobs/services/uploads/upload-sessions.ts:readUploadSession
    seam: sync
    on_failure: >-
      Resend missing parts; missing multipart state is recorded as expired on
      read.
    left_inconsistent: Incomplete parts remain until completion or bucket abandonment.
    observer: The wizard's upload progress.
    provenance: observed
    source: 'react-console:lib/api/data-jobs/multipart.ts'
  - 'n': 4
    actor_or_service: Upload route and session service (complete)
    action: >-
      Assemble the object, check stored size and available digest, then record
      the exact storage version.
    anchor: >-
      services-platform:modules/data-jobs/services/uploads/upload-sessions.ts:completeUpload
    seam: sync
    on_failure: >-
      Missing parts keep the session open; size or digest mismatch marks it
      aborted.
    left_inconsistent: >-
      Assembly can outlive a bookkeeping crash; an integrity-refused object also
      remains stored.
    observer: The caller; the session state.
    provenance: test
    source: >-
      services-platform:modules/data-jobs/tests/integration/services/uploads/upload-sessions.integration.spec.js#refuses
      in words a completion naming a part S3 does not hold, rather than failing
      as a server fault
  - 'n': 5
    actor_or_service: Upload route (abort)
    action: Remove multipart work and settle an explicit abort.
    anchor: >-
      services-platform:modules/data-jobs/services/uploads/upload-sessions.ts:abortUpload
    seam: sync
    on_failure: Refuse completed sessions and return already abandoned sessions unchanged.
    left_inconsistent: Storage removal and row settlement are separate effects.
    observer: The request response exposes the result.
    provenance: test
    source: >-
      services-platform:modules/data-jobs/tests/integration/services/uploads/upload-sessions.integration.spec.js#refuses
      to abort a completed session, whose object a job may already be reading
idempotency: >-
  The service permits repeated abort of settled abandoned sessions, but refuses
  a second completed transfer. Replacement input needs a fresh session;
  interrupted transfer can resume while its multipart state remains available.
concurrency: >-
  Storage calls precede row-locked session settlement. No lock spans both
  systems, and concurrent operations can observe different storage and session
  states.
branches:
  - >-
    Above 256 MiB, a declared digest stays unverified unless storage supplies a
    full-object digest.
waits:
  - >-
    Part addresses expire after one hour; bucket cleanup abandons incomplete
    transfer after two days.
evidence:
  - >-
    services-platform at ad3f6a65: services/uploads/upload-sessions.ts,
    upload-storage.ts; services/persistence/uploads.ts;
    aws/api/data-jobs.routes.ts; docs/data-jobs/uploads.md.
  - >-
    react-console at baf6dae3: lib/api/data-jobs/uploads.ts, multipart.ts,
    sha256.ts; components/data-management/data-jobs/new/upload-progress.tsx.
controls: [TC-AVL-004, TC-AVL-005, TC-AZ-001, TC-AZ-002, TC-AZ-003, TC-AZ-004, TC-CHG-001, TC-CHG-002, TC-CHG-003, TC-CHG-004, TC-CHG-005, TC-CHG-006, TC-CHG-007, TC-CRY-001, TC-CRY-002, TC-CRY-003, TC-CRY-004, TC-DAT-001, TC-DAT-002, TC-DAT-003, TC-DAT-004, TC-DAT-005, TC-IAM-001, TC-IAM-002, TC-IAM-004, TC-IAM-005, TC-LOG-001, TC-LOG-004, TC-LOG-005, TC-PI-001, TC-PI-002, TC-PI-003, TC-PI-004, TC-PI-005, TC-TEN-001, TC-TEN-002, TC-TEN-003, TC-VEN-001, TC-VEN-002, TC-VEN-003]
---

# Upload A File In Signed Parts

## What It Does

The client transfers file parts directly to storage using short-lived signed addresses. Platform keeps session bookkeeping and verifies size plus the available digest before making the upload usable.

## The Flow

```mermaid
flowchart TD
  H1["Upload route and session service (open)"] --> H2
  H2["Upload route (parts)"] --> H3
  H3["Client and storage"] --> H4
  H4["Upload route and session service (complete)"] --> H5
  H5["Upload route (abort)"]
```

## When Something Fails

Storage refusal and database failure have separate outcomes. In particular, completed assembly followed by a process stop can later appear as expiry rather than repaired completion. Large-file integrity remains a product decision.

## Open Design Questions

Large-file integrity and storage-first crash repair need explicit decisions.
