<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Files on back(d)</title><link>https://fernandezvara.github.io/backd/docs/build/files/</link><description>Recent content in Files on back(d)</description><generator>Hugo</generator><language>en-us</language><atom:link href="https://fernandezvara.github.io/backd/docs/build/files/index.xml" rel="self" type="application/rss+xml"/><item><title>Files in functions</title><link>https://fernandezvara.github.io/backd/docs/build/files/functions/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://fernandezvara.github.io/backd/docs/build/files/functions/</guid><description>&lt;p&gt;A function reaches the files of a document through the same client it uses for data: &lt;code&gt;files(id, field)&lt;/code&gt; on a collection of &lt;code&gt;ctx.db&lt;/code&gt; or &lt;code&gt;ctx.admin.db&lt;/code&gt;. It makes the same requests as any client, to the &lt;a href="../transfers/"&gt;&lt;code&gt;_files&lt;/code&gt; routes&lt;/a&gt;, so the same limits, type detection, journal and usage totals apply.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;photos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;main&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;people&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;personId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;photo&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;photos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ada.png&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;image/png&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="c1"&gt;// the updated document
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;photos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// { file, response, bytes(), text() }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;link&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;photos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// { url, expires_at }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;photos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// the field&amp;#39;s files; delete(fileId) removes one
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;put(data, { name, type, ifMatch })&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Stores a file (a string, bytes, a &lt;code&gt;Blob&lt;/code&gt; or a stream): it replaces a single field&amp;rsquo;s file and is added to a &lt;code&gt;multiple&lt;/code&gt; field&amp;rsquo;s. Resolves with the document, which now holds the file&amp;rsquo;s details. &lt;code&gt;type&lt;/code&gt; is a hint: backd detects the real one from the content&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;get(fileId?)&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Downloads a file. Without &lt;code&gt;fileId&lt;/code&gt;, the one a single field holds; a field with several needs one. The bytes come through backd, even where downloads are redirects for everyone else, because a function can&amp;rsquo;t reach the bucket&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;link(fileId?)&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;{ url, expires_at }&lt;/code&gt;, exactly like &lt;code&gt;?link=json&lt;/code&gt;: for the app to open. A link of the storage is for the bucket; a &lt;a href="../transfers/#links-for-apps"&gt;backd link&lt;/a&gt; needs &lt;code&gt;BACKD_URL&lt;/code&gt;, so it can name backd&amp;rsquo;s public address&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;delete(fileId?, { ifMatch })&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Removes one file, or every file of the field. Resolves with the document&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="as-the-caller-or-as-the-function"&gt;As the caller, or as the function&lt;a class="hd-anchor" href="#as-the-caller-or-as-the-function" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;ctx.db&lt;/code&gt; acts as the caller: &lt;code&gt;put&lt;/code&gt; and &lt;code&gt;delete&lt;/code&gt; are asked of the document&amp;rsquo;s &lt;code&gt;update&lt;/code&gt; rule (with &lt;code&gt;data&lt;/code&gt; and &lt;code&gt;changed()&lt;/code&gt; as for an upload), &lt;code&gt;get&lt;/code&gt; and &lt;code&gt;link&lt;/code&gt; of the &lt;code&gt;read&lt;/code&gt; rule, and an upload is recorded as theirs. A document the caller can&amp;rsquo;t read is a &lt;code&gt;NotFoundError&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>File fields</title><link>https://fernandezvara.github.io/backd/docs/build/files/file-fields/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://fernandezvara.github.io/backd/docs/build/files/file-fields/</guid><description>&lt;p&gt;A collection says which fields of its documents carry files under &lt;code&gt;files:&lt;/code&gt; in its &lt;a href="../../configuration/config-dir/#collectionyaml"&gt;&lt;code&gt;collection.yaml&lt;/code&gt;&lt;/a&gt;. The realm needs a &lt;a href="../../../operate/providers/storage/"&gt;&lt;code&gt;storage:&lt;/code&gt;&lt;/a&gt; section.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;avatar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;max_size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;2MiB&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;types&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="l"&gt;image/png, image/jpeg, image/webp]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;receipts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;multiple&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;max_files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;max_size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;10MiB&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;types&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="l"&gt;application/pdf, image/*]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;manual&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;max_size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;20MiB&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;download&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;proxy&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;1h&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Key&lt;/th&gt;
 &lt;th&gt;Default&lt;/th&gt;
 &lt;th&gt;Meaning&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;max_size&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;required&lt;/td&gt;
 &lt;td&gt;The largest one file may be (&lt;code&gt;512KiB&lt;/code&gt;, &lt;code&gt;10MiB&lt;/code&gt;, &lt;code&gt;1GiB&lt;/code&gt;). Checked while the bytes stream in&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;types&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;any&lt;/td&gt;
 &lt;td&gt;Allowed content types, &lt;code&gt;image/png&lt;/code&gt; or a family such as &lt;code&gt;image/*&lt;/code&gt;. Checked against the type &lt;strong&gt;detected from the file&amp;rsquo;s content&lt;/strong&gt;, never the one the client claims. A family never admits SVG, HTML or XML: name them to accept them&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;multiple&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;The field is a list of files instead of one&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;max_files&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;10&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;With &lt;code&gt;multiple&lt;/code&gt;: how many files the field may hold (1 to 1000)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;upload&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;proxy&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;proxy&lt;/code&gt; streams the bytes through backd; &lt;code&gt;direct&lt;/code&gt; has the client send them straight to the bucket with a signed link, which backd verifies afterwards (&lt;a href="../transfers/#direct-uploads"&gt;Direct uploads&lt;/a&gt;). &lt;code&gt;max_size&lt;/code&gt; is at most 5GiB for &lt;code&gt;direct&lt;/code&gt;, or 4TiB with a provider that takes &lt;a href="../transfers/#files-above-5-gib"&gt;uploads in parts&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;download&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;the realm&amp;rsquo;s&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;presigned&lt;/code&gt; redirects to a short-lived link of the storage; &lt;code&gt;proxy&lt;/code&gt; streams the file through backd&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;presigned_ttl&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;the realm&amp;rsquo;s&lt;/td&gt;
 &lt;td&gt;How long this field&amp;rsquo;s links last (at most 7 days)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;cache&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;none&lt;/td&gt;
 &lt;td&gt;With &lt;code&gt;download: proxy&lt;/code&gt;: how long shared caches may keep the file, only when anyone may read the document&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;keep_metadata&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;By default the metadata of JPEG and PNG pictures is &lt;a href="#metadata-and-the-gps-risk"&gt;removed&lt;/a&gt; when they are stored. &lt;code&gt;true&lt;/code&gt; stores them exactly as sent&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;max_pixels&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;the instance&amp;rsquo;s&lt;/td&gt;
 &lt;td&gt;For images: the most pixels (width × height) the field gets &lt;a href="#image-versions"&gt;versions&lt;/a&gt; for. It can only lower &lt;code&gt;BACKD_IMAGE_MAX_PIXELS&lt;/code&gt;; a higher value stops startup&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;versions&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;none&lt;/td&gt;
 &lt;td&gt;Resized copies of an image, declared by name: see &lt;a href="#image-versions"&gt;Image versions&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;A field name is lower-case letters, digits and underscores. &lt;strong&gt;A file field is declared only here:&lt;/strong&gt; one that is also in &lt;code&gt;schema.json&lt;/code&gt; is a startup error, because backd adds its schema itself, to the validators in MongoDB, to the &lt;a href="../rules/"&gt;rules&lt;/a&gt; and to the queries.&lt;/p&gt;</description></item><item><title>Uploads and downloads</title><link>https://fernandezvara.github.io/backd/docs/build/files/transfers/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://fernandezvara.github.io/backd/docs/build/files/transfers/</guid><description>&lt;p&gt;Every file operation is an operation on a document and follows its &lt;a href="../rules/"&gt;access rules&lt;/a&gt;: the &lt;code&gt;update&lt;/code&gt; rule for uploads and removals, the &lt;code&gt;read&lt;/code&gt; rule for downloads. A document the caller can&amp;rsquo;t read answers &lt;code&gt;404&lt;/code&gt;, never revealing its files. &lt;a href="../../auth/api-keys/"&gt;API keys&lt;/a&gt; and the &lt;a href="../../api/documents/"&gt;admin data route&lt;/a&gt; skip rules, as everywhere.&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Operation&lt;/th&gt;
 &lt;th&gt;Request&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Upload&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;POST /v1/{realm}/{database}/{collection}/{id}/_files/{field}&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Download&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;GET …/{id}/_files/{field}/{file id}&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Remove one&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;DELETE …/{id}/_files/{field}/{file id}&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Clear the field&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;DELETE …/{id}/_files/{field}&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The same four exist under &lt;code&gt;/v1/{realm}/_admin/data/…&lt;/code&gt;. There is no &lt;code&gt;PUT&lt;/code&gt;: a file is replaced by uploading another.&lt;/p&gt;</description></item><item><title>Rules for files</title><link>https://fernandezvara.github.io/backd/docs/build/files/rules/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://fernandezvara.github.io/backd/docs/build/files/rules/</guid><description>&lt;p&gt;Files have no rules of their own: they follow the &lt;a href="../../auth/rules/"&gt;rules of their document&lt;/a&gt;.&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Operation&lt;/th&gt;
 &lt;th&gt;Rule&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Upload, remove one, clear a field&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;update&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Download, &lt;code&gt;?link=json&lt;/code&gt;, &lt;code&gt;?file_links=true&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;read&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;File fields are known to the rules like any other field of the collection, so a rule can read them from &lt;code&gt;document&lt;/code&gt; and &lt;code&gt;data&lt;/code&gt;. For a file operation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;document&lt;/code&gt; is the document as stored; &lt;code&gt;data&lt;/code&gt; is the document &lt;strong&gt;as it would be after the change&lt;/strong&gt;; &lt;code&gt;changed()&lt;/code&gt; lists the changed fields, which includes the file field.&lt;/li&gt;
&lt;li&gt;The rule runs &lt;strong&gt;before any byte is stored&lt;/strong&gt;: a refused upload costs nothing and leaves nothing behind.&lt;/li&gt;
&lt;li&gt;API keys and &lt;code&gt;ctx.admin.db&lt;/code&gt; in functions skip rules.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="reserving-a-field-for-functions"&gt;Reserving a field for functions&lt;a class="hd-anchor" href="#reserving-a-field-for-functions" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;A field only a function may set, such as a generated thumbnail, is a file field the rule refuses to clients:&lt;/p&gt;</description></item><item><title>Files in apps</title><link>https://fernandezvara.github.io/backd/docs/build/files/apps/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://fernandezvara.github.io/backd/docs/build/files/apps/</guid><description>&lt;p&gt;An app reaches files through the &lt;a href="../../clients/js/#files"&gt;JavaScript client&lt;/a&gt;, which makes the requests described in &lt;a href="../transfers/"&gt;Uploads and downloads&lt;/a&gt;. This page is about what to do with them in a page.&lt;/p&gt;
&lt;h2 id="uploading"&gt;Uploading&lt;a class="hd-anchor" href="#uploading" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;uploadFile(id, field, file, { onProgress })&lt;/code&gt; puts a file in a document, and &lt;code&gt;prepareUpload(field, file)&lt;/code&gt; makes one for a document that doesn&amp;rsquo;t exist yet: name its &lt;code&gt;ref&lt;/code&gt; in the create. Both work whether the field takes uploads through backd or straight to the bucket; the client asks the field (&lt;code&gt;fileField(field)&lt;/code&gt;), which also tells the page &lt;code&gt;max_size&lt;/code&gt;, &lt;code&gt;types&lt;/code&gt; and &lt;code&gt;max_files&lt;/code&gt; to check a file before sending it.&lt;/p&gt;</description></item><item><title>Security of files</title><link>https://fernandezvara.github.io/backd/docs/build/files/security/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://fernandezvara.github.io/backd/docs/build/files/security/</guid><description>&lt;p&gt;The files feature was reviewed as a whole before this page: rules on file operations, storage credentials and the endpoint restriction, both upload modes, type detection, links, proxied downloads, pending uploads and their tokens, the journal and deletion queue, reconcile, erasure and the functions API. This page says what came out of it.&lt;/p&gt;
&lt;h2 id="how-files-are-protected"&gt;How files are protected&lt;a class="hd-anchor" href="#how-files-are-protected" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The bucket is private, and only backd reaches it.&lt;/strong&gt; Files are served through backd, or through a link backd signed for one file. backd connects only to the realm&amp;rsquo;s declared &lt;code&gt;endpoint&lt;/code&gt; (checked after DNS resolution, with no redirects and no proxy); &lt;code&gt;public_endpoint&lt;/code&gt; is only written into links and is never connected to.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A file is as private as its document.&lt;/strong&gt; An upload, a removal and a direct-upload start are &lt;code&gt;update&lt;/code&gt; operations; a download, a link and &lt;code&gt;?file_links=true&lt;/code&gt; are &lt;code&gt;read&lt;/code&gt; operations; a document the caller can&amp;rsquo;t read answers &lt;code&gt;404&lt;/code&gt; for all of them, never revealing its files. The rule runs &lt;strong&gt;before any byte is stored&lt;/strong&gt;, with &lt;code&gt;data&lt;/code&gt; as the document will be, and again when the file is attached. See &lt;a href="../rules/"&gt;Rules for files&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Clients never write what describes a file.&lt;/strong&gt; The id, name, size, detected type, SHA-256 and time are backd&amp;rsquo;s: a client&amp;rsquo;s write drops them, and a PUT keeps the stored files. The object key is &lt;code&gt;&amp;lt;prefix&amp;gt;/&amp;lt;realm&amp;gt;/&amp;lt;database&amp;gt;/&amp;lt;collection&amp;gt;/&amp;lt;file id&amp;gt;&lt;/code&gt;: no name, and no part of a request, ever reaches it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The type is what the bytes say.&lt;/strong&gt; It is detected from the content, never the client&amp;rsquo;s claim, and &lt;code&gt;types&lt;/code&gt; is checked against it. &lt;strong&gt;A family such as &lt;code&gt;image/*&lt;/code&gt; or &lt;code&gt;text/*&lt;/code&gt; does not admit active content&lt;/strong&gt; (SVG, HTML, XML, scripts): a field takes one only when it names it. A field with no &lt;code&gt;types&lt;/code&gt; takes anything, and serves it only as a download.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Downloads can&amp;rsquo;t run in your origin.&lt;/strong&gt; A presigned link comes from the storage&amp;rsquo;s origin, never backd&amp;rsquo;s, and forces &lt;code&gt;attachment&lt;/code&gt;. A proxied download carries &lt;code&gt;Content-Disposition: attachment&lt;/code&gt;, &lt;code&gt;X-Content-Type-Options: nosniff&lt;/code&gt; and &lt;code&gt;Content-Security-Policy: sandbox&lt;/code&gt;. Names are cleaned (no path, no control or direction characters, 255 bytes) and quoted, so a name can&amp;rsquo;t add a header or leave its quotes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Direct uploads are verified, not trusted.&lt;/strong&gt; The link has the length, the type and the SHA-256 signed in, so the storage refuses another body; completing then checks the size and the checksum the &lt;em&gt;storage&lt;/em&gt; computed (a missing one fails) and detects the type from the first bytes. A mismatch deletes the object. A file sent in &lt;a href="../transfers/#files-above-5-gib"&gt;parts&lt;/a&gt; is verified the same way, part by part and as a whole: every part has its own SHA-256 signed in, and the composite checksum the storage computes is checked against the declared parts. The &lt;code&gt;sha256&lt;/code&gt; recorded for it is that composite&amp;rsquo;s digest, not the SHA-256 of its bytes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tokens are secrets.&lt;/strong&gt; An upload token is 256 random bits, only its SHA-256 is stored, it is compared in constant time, used once, bound to a collection and field and, when a signed-in user made it, to that user, and never logged. An upload id alone attaches or completes nothing, and every wrong, used, expired or foreign reference answers the same &lt;code&gt;400&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;backd&amp;rsquo;s own links&lt;/strong&gt; are an HMAC over the realm, database, collection, document, field, file and expiry, with a per-realm key, verified in constant time: a link can&amp;rsquo;t be moved to another file or kept past its time. Rotating the key (setting or deleting the secret &lt;code&gt;BACKD_FILES_LINK_KEY&lt;/code&gt;) invalidates every one.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Backd&amp;rsquo;s keys are not a function&amp;rsquo;s to read.&lt;/strong&gt; A function can&amp;rsquo;t declare the realm&amp;rsquo;s storage secrets or the link key as its own secrets: the configuration is refused at startup.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cleanup never touches what a document holds.&lt;/strong&gt; A worker checks that no document references an upload before it deletes it, and reconcile only considers objects named like files, older than 24 hours, with no open upload record, and that no declared field references.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Metadata is removed from pictures.&lt;/strong&gt; The GPS position, camera and comments of a JPEG, and the text and time of a PNG, are dropped as it is stored, unless the field sets &lt;code&gt;keep_metadata: true&lt;/code&gt;: see &lt;a href="../file-fields/#metadata-and-the-gps-risk"&gt;Metadata and the GPS risk&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Limits&lt;/strong&gt; bound what a caller can store: &lt;code&gt;max_size&lt;/code&gt; and &lt;code&gt;max_files&lt;/code&gt; per field, &lt;code&gt;BACKD_MAX_UPLOAD_BYTES&lt;/code&gt; for proxy uploads, 20 unused uploads per caller (IPv6 callers counted per &lt;code&gt;/64&lt;/code&gt;) and a rate for starting them.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="what-you-are-responsible-for"&gt;What you are responsible for&lt;a class="hd-anchor" href="#what-you-are-responsible-for" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The bucket and its key.&lt;/strong&gt; Keep it private, give the key only what your provider&amp;rsquo;s page lists, on the realm&amp;rsquo;s prefix; encryption at rest is the bucket&amp;rsquo;s setting. &lt;strong&gt;Give each backd instance its own &lt;code&gt;prefix&lt;/code&gt;:&lt;/strong&gt; two instances on one prefix collide, and a &lt;a href="../../../operate/storage-maintenance/#reconciling"&gt;reconcile&lt;/a&gt; on one deletes the other&amp;rsquo;s files.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Links outlive rules.&lt;/strong&gt; A storage link and a backd link both work for anyone who has them until they expire, even if the document&amp;rsquo;s rules change meanwhile. Keep &lt;code&gt;presigned_ttl&lt;/code&gt; short on sensitive fields; the default is five minutes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Caches outlive rules too.&lt;/strong&gt; With &lt;code&gt;download: proxy&lt;/code&gt; and &lt;code&gt;cache&lt;/code&gt;, a shared cache may keep a file of a document anyone can read, and keeps it after you tighten the rule. Use &lt;code&gt;cache&lt;/code&gt; only for files meant to be public.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Set &lt;code&gt;BACKD_URL&lt;/code&gt; in production.&lt;/strong&gt; backd-signed links name backd&amp;rsquo;s public address; without it they are built from the request&amp;rsquo;s &lt;code&gt;Host&lt;/code&gt; header, which a caller chooses.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Anonymous uploads cost storage.&lt;/strong&gt; Where a collection&amp;rsquo;s &lt;code&gt;create&lt;/code&gt; rule admits anonymous callers, each address may hold 20 unused uploads of up to &lt;code&gt;max_size&lt;/code&gt; for an hour. Keep &lt;code&gt;max_size&lt;/code&gt; small on such fields and let the edge rate-limit by address. Nothing scans files for malware.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A removed file field leaves trusted-looking data.&lt;/strong&gt; If you take a field out of &lt;code&gt;files:&lt;/code&gt; and later declare it again, what the documents still hold in it is read as file details again. Don&amp;rsquo;t re-use a field name for something else; clear it first.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Functions with &lt;code&gt;ctx.admin.db&lt;/code&gt; skip the rules&lt;/strong&gt;, files included: review them as privileged code.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Backups.&lt;/strong&gt; MongoDB backups don&amp;rsquo;t include files: back the bucket up at the provider and run reconcile after a restore.&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>