Host file adapter

Upload to your object store. Persist ref, filename, media type, byte size, and sha256 on the RFQ record. Resolve a short-lived download URL only after the caller may see that RFQ and that uploader role. Bootstrapware does not scan files and does not fetch arbitrary URLs.

// examples/rfq-nextjs/server/files.ts
export async function putRfqObject(file: File, actorId: string) {
  const ref = `rfq/${actorId}/${crypto.randomUUID()}`;
  await objectStore.put(ref, file); // your bucket
  return {
    ref,
    filename: file.name.slice(0, 200),
    mediaType: file.type || "application/octet-stream",
    bytes: file.size,
    sha256: await sha256hex(file),
  };
}

export async function resolveRfqDownload(input: {
  ref: string;
  actorId: string;
  rfqId: string;
  role: "buyer" | "supplier";
}) {
  await assertAttachmentAccess(input); // same RFQ, same role, not a foreign supplier
  return objectStore.signedGet(input.ref, { expiresInSec: 60 });
}

Pass the metadata object to adapter.addAttachment. Never send bytes, dataUrl, or a long-lived URL. Failed uploads should delete the object. Unavailable files show as a missing reference, not a fetched remote page.

Related: Attachment contract · Notifications