Flow
ServicesDocuments Service

Documents Service

Stores documents in Azure Blob Storage, keeps their references and metadata in Postgres, and hands out short-lived SAS URLs.

The documents service holds files for every other service. Postgres owns the record — where the blob is, what it is called, what it is attached to — and Azure Blob Storage holds the bytes. No file content passes through this service in either direction: callers upload to and download from a SAS URL: A Shared Access Signature: a blob URL carrying a signed, time-limited grant of specific permissions. The documents service signs one per request with the storage account key and never stores it, so an expired URL cannot be replayed from a command response. that the service signs and that expires on its own.

Domain
Services
Kind
Go HTTP service
Lifecycle
Active
flowctl project
None
Stacks
Runs on its own

Uploading takes two calls

A caller asks for a write SAS, PUTs the bytes to it, then tells this service the upload happened. The bytes never traverse this service, so a large file costs it nothing and an interrupted upload leaves no record behind.

Diagram. Text representation: sequenceDiagram participant Caller participant API as documents-api participant PG as Postgres participant Blob as Azure Blob Storage Caller->>API: POST /v1/documents/sas API-->>Caller: write SAS, blob_name, expiry Caller->>Blob: PUT bytes to the SAS URL Caller->>API: POST /v1/documents (blob_name) API->>Blob: does the blob exist? API->>PG: insert document, references, metadata, outbox event API-->>Caller: the document record
Diagram source
sequenceDiagram
  participant Caller
  participant API as documents-api
  participant PG as Postgres
  participant Blob as Azure Blob Storage

  Caller->>API: POST /v1/documents/sas
  API-->>Caller: write SAS, blob_name, expiry
  Caller->>Blob: PUT bytes to the SAS URL
  Caller->>API: POST /v1/documents (blob_name)
  API->>Blob: does the blob exist?
  API->>PG: insert document, references, metadata, outbox event
  API-->>Caller: the document record

The blob name is {stem}_{uuidv7}.{ext}, chosen by the service rather than the caller, so two uploads of lease.pdf cannot overwrite each other and a caller cannot aim an upload at a blob that already belongs to another document.

What it owns

  • Document records, with polymorphic references (ref_type, ref_id, ref_category, is_external) and meta_name/meta_value metadata
  • The storage-account catalog: which accounts exist, their visibility, containers, and SAS lifetime
  • SAS signing for uploads and downloads
  • The documents outbox and its documents.created / documents.updated events

What it does not own

The meaning of a reference. ref_type is a domain name and ref_id an id within it; this service never resolves either, and does not check that the referenced entity exists. A caller that wants its documents asks for them by the ids it already knows.

It also does not own file content lifecycle beyond the record: replacing a blob's bytes is not an operation today, and neither is deletion.

Authorization is not wired yet, matching services/template/README.md. Audit columns use a placeholder actor (system).

Canonical service README: services/documents/README.md.

On this page