Understand the encrypted transfer model

The service generates a random payload key, encrypts file chunks and the manifest locally with XChaCha20-Poly1305, and wraps the payload key for eligible recipients. ZipPigeon coordinates upload state and delivery metadata, but ordinary service upload calls should not send plaintext files or raw payload keys to the API.

  • File chunks and the manifest are authenticated ciphertext before storage.
  • Recipient and service key envelopes are separate from the encrypted file blobs.
  • Operational metadata such as size, status, timestamps, and identifiers is still processed.

Create, transfer, and complete an upload

POST the encrypted transfer shape to create an upload and receive a destination for each expected part. PUT each already-encrypted part to its destination, then complete the upload with the encrypted manifest, owner key envelope, and verified part details. Creation and completion deliberately use separate scopes so a pipeline can split duties.

POST /api/uploads                         # uploads:create
PUT  <returned part destination>              # encrypted bytes
POST /api/uploads/:id/complete                # uploads:complete
GET  /api/uploads/:id                         # uploads:read

List and retrieve service-owned transfers

Use uploads:read to list or inspect uploads owned by that service identity. Use downloads:read to request single-use encrypted part paths for an active completed upload, fetch those ciphertext parts, verify them, and decrypt locally with the service-owned payload-key envelope.

GET /api/uploads                          # uploads:read
GET /api/uploads/:id                      # uploads:read
GET /api/uploads/:id/parts                # downloads:read
GET /api/download-grants/:grant/:part     # short-lived encrypted part

Design retries around phase boundaries

Persist the upload ID and completed-part state in the job system. A failed encrypted-part transfer can be retried without inventing a second logical package, while completion should run only after every expected part is present and verified. Treat an expired or already-completed transfer as a lifecycle result rather than blindly restarting it.

  • Generate nonces and encryption material once per intended encrypted object, not once per network retry.
  • Do not log returned secure links, bearer tokens, payload keys, or encrypted key-bundle passphrases.
  • Keep part-count and upload-size policy visible to the producer before it begins expensive work.

Benefits and use cases

The workflow separates data-plane transfer from plaintext access: object storage receives encrypted parts, service credentials stay in the backend, and decryption remains local. It supports generated financial reports, legal exports, build artifacts, incident evidence, database extracts, and scheduled customer deliveries.

  • Report delivery: encrypt a nightly export before it leaves the reporting worker.
  • Evidence collection: preserve encrypted diagnostic bundles while limiting which service can retrieve them.
  • Large payloads: retry individual ciphertext parts instead of restarting the entire transfer.

Use the CLI before writing a custom client

The standalone CLI already implements encryption, signing, upload completion, download verification, and local decryption. Start there for shell jobs and prove the workflow before reproducing the cryptographic protocol in another runtime. A custom client must preserve the same signed-request and envelope boundaries.

zippigeon upload ./report.pdf --secure-link --one-time --json
zippigeon upload ./report.pdf --recipient recipient@example.com
zippigeon download --upload <upload_id> --out ./downloads