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