Skip to content
Save my spot

Architecture: control and file data are separate

A transfer has two jobs: decide who may send what to whom, and move the encrypted bytes. pipeit keeps them apart.

A transfer has two jobs: to clarify who may send what to whom, and to bring the encrypted bytes to the receiver. pipeit separates these jobs. The online API manages accounts, devices, releases, jobs and states. The data path can run directly between endpoints or through the relay (server route). Switching the data path does not automatically change the receiver or the meaning of the job.

DiagramControl and file data take separate pathsDiagram: sender and receiver talk to the pipeit API for sign-in, releases, acceptance and receipts. The encrypted file data goes directly from sender to receiver, or alternatively through a relay that does not store a file. The API uses a database, a store for limits and background work.
  1. SenderSign-in, releases, transfer orderControl and permissionspipeit API
  2. ReceiverAcceptance, status, receiptsControl and permissionspipeit API
  3. SenderEncrypted file data, directlyFile dataReceiver
  4. SenderAlternative: encrypted framesAlternative pathRelay
  5. RelayForwarded without storing a fileAlternative pathReceiver
  6. pipeit APIDurable stateControl and permissionsDatabase
  7. pipeit APIBounded budgets and leasesControl and permissionsLimits store
  8. pipeit APIEvents and jobsControl and permissionsBackground work

The two data paths are alternatives, not a parallel double send. Even with direct online file transport, the API can be needed for authorization, setting up the connection and status. Nearby transfers between the pipeit apps and the local pairing of native clients work without the online API; the receipt is synced the next time the device is online.

Building blockIts jobNot its job
Go/Chi APIBusiness rules, permissions, states and HTTP contractsDecrypting file contents of a normal end-to-end transfer
PostgreSQL and sqlcDurable business state, versions, atomic decisionsStoring the payload of all live transfers as blobs
RedisBounded shared budgets and occupancy or leasesAn unbounded file queue
NATS/JetStream and workersEvent processing and background tasksThe file byte stream of a transfer
WebSocket and native transport adaptersMoving encrypted frames between authorized endpointsInventing account rights of their own
S3-compatible storageStorage for separate profiles, including pipeit Cloud, and operational storage tasksAn automatic backup of every current live transfer
React/TypeScript web app and native clientsSelection, local keys, cryptography, file access, operationA different meaning of “delivered”
OpenAPI and the shared Go SDKVersioned interfaces and reusable native flowsA substitute for each client's own file and key handling

Scroll sideways to see the whole table.

Why business logic stays central

Every client shows the same transfer state the same way. A server daemon must not read the same receiver block as a success that shows as an error in the browser. Conversely, the backend cannot check whether a local folder is writable under the service user. That check belongs in the shared native file library; the receive release and the allowed actions remain part of the shared contract.