<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>akeil.de</title><link>https://akeil.de/</link><description>akeil's website.</description><atom:link href="https://akeil.de/rss.xml" rel="self" type="application/rss+xml"></atom:link><language>en</language><lastBuildDate>Sat, 02 Jan 2021 21:07:04 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>reMarkable Cloud API</title><link>https://akeil.de/posts/remarkable-cloud-api/</link><dc:creator>Alexander Keil</dc:creator><description>&lt;div class="section" id="remarkable-cloud-api"&gt;
&lt;h2&gt;reMarkable Cloud API&lt;/h2&gt;
&lt;p&gt;This is an unofficial documentation for the cloud based sync service that
comes with the &lt;a class="reference external" href="https://remarkable.com/"&gt;reMarkable&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/remarkable-cloud-api/#id3" id="id4"&gt;1&lt;/a&gt; tablet.&lt;/p&gt;
&lt;p&gt;This document is based on an &lt;a class="reference external" href="https://github.com/splitbrain/ReMarkableAPI"&gt;existing documentation&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/remarkable-cloud-api/#id8" id="id9"&gt;3&lt;/a&gt;,
the existing implementation in &lt;a class="reference external" href="https://github.com/juruen/rmapi"&gt;rmapi&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/remarkable-cloud-api/#id10" id="id11"&gt;4&lt;/a&gt;
and observations made while implementing &lt;a class="reference external" href="https://github.com/akeil/rmtool"&gt;my own client&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/remarkable-cloud-api/#id12" id="id13"&gt;5&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The service is used to sync content from the tablet to a remote service
and make it available on other devices.
One can also upload new content to the cloud service and it will be synced
to the device.&lt;/p&gt;
&lt;!-- TEASER_END --&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title"&gt;Disclaimer&lt;/p&gt;
&lt;p&gt;This is a hobby project and &lt;strong&gt;not&lt;/strong&gt; associated with the &lt;em&gt;reMarkable&lt;/em&gt;
company.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The API is a simple ReST oriented web service using JSON as a transfer format.
The content of the notes is very close to the &lt;a class="reference external" href="https://remarkablewiki.com/tech/filesystem"&gt;file based structure&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/remarkable-cloud-api/#id5" id="id6"&gt;2&lt;/a&gt; which
can be found on the tablet itself.&lt;/p&gt;
&lt;p&gt;The API is split into separate parts:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Authentication&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Storage&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Notifications&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="section" id="authentication"&gt;
&lt;h3&gt;Authentication&lt;/h3&gt;
&lt;p&gt;Users authenticate by logging into the reMarkable website and generating
a one-time code. That code can be used to "register" a new device and
request a &lt;em&gt;Device Token&lt;/em&gt; from the API.
That device token can then be used to request a &lt;em&gt;User Token&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The user token is used to authorize requests against the API
The user tokens expire after some time (less 24h?).
The assumption is to generate a new user token for each session.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Host:&lt;/strong&gt;
&lt;a class="reference external" href="https://my.remarkable.com"&gt;https://my.remarkable.com&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;colgroup&gt;
&lt;col style="width: 30%"&gt;
&lt;col style="width: 70%"&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th class="head"&gt;&lt;p&gt;Endpoint&lt;/p&gt;&lt;/th&gt;
&lt;th class="head"&gt;&lt;p&gt;URL&lt;/p&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;p&gt;Device Token&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;&lt;code class="docutils literal"&gt;/token/json/2/device/new&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;p&gt;User Token&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;&lt;code class="docutils literal"&gt;/token/json/2/user/new&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class="section" id="one-time-code"&gt;
&lt;h4&gt;One Time Code&lt;/h4&gt;
&lt;p&gt;There are two URLs for generating the one time code:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://my.remarkable.com/connect/desktop"&gt;https://my.remarkable.com/connect/desktop&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://my.remarkable.com/connect/mobile"&gt;https://my.remarkable.com/connect/mobile&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A user must be logged in with his or her reMarkable account to generate a one
time code.&lt;/p&gt;
&lt;p&gt;The code is alphanumeric with 8 digits (e.g: &lt;code class="docutils literal"&gt;apwngead&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;The code can be used for one request, whether it is successful or not.
Users can create as many codes as they want.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Not sure how long a code is valid.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Not sure in which way the &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;.../desktop&lt;/span&gt;&lt;/code&gt;
and &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;.../mobile&lt;/span&gt;&lt;/code&gt; URLs are different.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="device-token"&gt;
&lt;h4&gt;Device Token&lt;/h4&gt;
&lt;p&gt;The device token can be requested by sending a &lt;code class="docutils literal"&gt;POST&lt;/code&gt; request against the
authentication API:&lt;/p&gt;
&lt;pre class="literal-block"&gt;POST /token/json/2/device/new&lt;/pre&gt;
&lt;p&gt;with the following JSON body:&lt;/p&gt;
&lt;pre class="code json"&gt;&lt;a name="rest_code_483942a7aad948f0b7080212bb2b511a-1"&gt;&lt;/a&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;a name="rest_code_483942a7aad948f0b7080212bb2b511a-2"&gt;&lt;/a&gt;  &lt;span class="nt"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"apwngead"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_483942a7aad948f0b7080212bb2b511a-3"&gt;&lt;/a&gt;  &lt;span class="nt"&gt;"deviceDesc"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"desktop-windows"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_483942a7aad948f0b7080212bb2b511a-4"&gt;&lt;/a&gt;  &lt;span class="nt"&gt;"deviceID"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"d4605307-a145-48d2-b60a-3be2c46035ef"&lt;/span&gt;
&lt;a name="rest_code_483942a7aad948f0b7080212bb2b511a-5"&gt;&lt;/a&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;The response is the token as a &lt;code class="docutils literal"&gt;text/plain&lt;/code&gt; response body.&lt;/p&gt;
&lt;p&gt;Unsuccessful requests contain a plain text error message in the response body.&lt;/p&gt;
&lt;table&gt;
&lt;colgroup&gt;
&lt;col style="width: 30%"&gt;
&lt;col style="width: 70%"&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th class="head"&gt;&lt;p&gt;Field&lt;/p&gt;&lt;/th&gt;
&lt;th class="head"&gt;&lt;p&gt;Description&lt;/p&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;p&gt;code&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;the one time code&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;p&gt;deviceDesc&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;enum, see below&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;p&gt;deviceID&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;any UUID, generate one&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Values for &lt;cite&gt;deviceDesc&lt;/cite&gt; are:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;desktop-windows&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;desktop-macos&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;mobile-android&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;mobile-ios&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;browser-chrome&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal"&gt;remarkable&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;Not sure if the device description has any effect on the behavior of the
service but invalid values are rejected by the API.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code class="docutils literal"&gt;deviceID&lt;/code&gt; is used in the notifications API,
presumably to allow clients to recognize notifications that were caused
by their own actions.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="user-token"&gt;
&lt;h4&gt;User Token&lt;/h4&gt;
&lt;p&gt;To request a user token send an empty &lt;code class="docutils literal"&gt;POST&lt;/code&gt; request against the
authentication API, using the device token as &lt;code class="docutils literal"&gt;Authorization: Bearer&lt;/code&gt; header.&lt;/p&gt;
&lt;pre class="literal-block"&gt;POST /token/json/2/user/new&lt;/pre&gt;
&lt;p&gt;On success, this will return the user token in &lt;code class="docutils literal"&gt;text/plain&lt;/code&gt;.
Again, the service returns error message as &lt;code class="docutils literal"&gt;text/plain&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="discovery"&gt;
&lt;h3&gt;Discovery&lt;/h3&gt;
&lt;p&gt;The host for the &lt;em&gt;Storage&lt;/em&gt; and &lt;em&gt;Notification&lt;/em&gt; API is different from the
authentication host.
It is determined dynamically by requesting it from a "service manager".&lt;/p&gt;
&lt;div class="section" id="storage"&gt;
&lt;h4&gt;Storage&lt;/h4&gt;
&lt;pre class="literal-block"&gt;GET https://service-manager-production-dot-remarkable-production.appspot.com/service/json/1/document-storage?environment=production&amp;amp;group=auth0%7C5a68dc51cb30df1234567890&amp;amp;apiVer=2&lt;/pre&gt;
&lt;p&gt;The URL parameters are as follows:&lt;/p&gt;
&lt;table&gt;
&lt;colgroup&gt;
&lt;col style="width: 24%"&gt;
&lt;col style="width: 76%"&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th class="head"&gt;&lt;p&gt;Name&lt;/p&gt;&lt;/th&gt;
&lt;th class="head"&gt;&lt;p&gt;Value&lt;/p&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;p&gt;environment&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;&lt;code class="docutils literal"&gt;production&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;p&gt;group&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;&lt;code class="docutils literal"&gt;auth0|5a68dc51cb30df1234567890&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;p&gt;apiVer&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;&lt;code class="docutils literal"&gt;2&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class="admonition note"&gt;
&lt;p class="admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;It seems that the &lt;cite&gt;group&lt;/cite&gt; parameter does not have any effect.
Responses are the same with or without that parameter.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The response looks like this:&lt;/p&gt;
&lt;pre class="code json"&gt;&lt;a name="rest_code_40205dd4cbcb4448b08c33508865b27c-1"&gt;&lt;/a&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;a name="rest_code_40205dd4cbcb4448b08c33508865b27c-2"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;"Status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"OK"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_40205dd4cbcb4448b08c33508865b27c-3"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;"Host"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"document-storage-production-dot-remarkable-production.appspot.com"&lt;/span&gt;
&lt;a name="rest_code_40205dd4cbcb4448b08c33508865b27c-4"&gt;&lt;/a&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;The &lt;code class="docutils literal"&gt;Status&lt;/code&gt; field can contain the text "OK" on success
and an error message on failure.&lt;/p&gt;
&lt;p&gt;This request is &lt;strong&gt;not authenticated&lt;/strong&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="notifications"&gt;
&lt;h4&gt;Notifications&lt;/h4&gt;
&lt;p&gt;Discovery for the Notification API works the same as for storage,
only the URL is different:&lt;/p&gt;
&lt;pre class="literal-block"&gt;GET https://service-manager-production-dot-remarkable-production.appspot.com/service/json/1/notifications?environment=production&amp;amp;group=auth0%7C5a68dc51cb30df1234567890&amp;amp;apiVer=1&lt;/pre&gt;
&lt;p&gt;Returns:&lt;/p&gt;
&lt;pre class="code json"&gt;&lt;a name="rest_code_caaa63ed994f4ca78a6c783033444e4a-1"&gt;&lt;/a&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;a name="rest_code_caaa63ed994f4ca78a6c783033444e4a-2"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;"Status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"OK"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_caaa63ed994f4ca78a6c783033444e4a-3"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;"Host"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"08z1-notifications-production.cloud.remarkable.engineering"&lt;/span&gt;
&lt;a name="rest_code_caaa63ed994f4ca78a6c783033444e4a-4"&gt;&lt;/a&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id1"&gt;
&lt;h3&gt;Storage&lt;/h3&gt;
&lt;p&gt;The content model for the API is a tree structure which consists of
"Notebooks" (leafs) and "Folders" (nodes). It resembles the structure of the
folders and notebooks found on the tablet.&lt;/p&gt;
&lt;p&gt;As with the tablet file system, the API returns a flat list and clients need
to create the tree structure by looking at the references to parent id of each
element.&lt;/p&gt;
&lt;table&gt;
&lt;colgroup&gt;
&lt;col style="width: 74%"&gt;
&lt;col style="width: 26%"&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th class="head"&gt;&lt;p&gt;Endpoint&lt;/p&gt;&lt;/th&gt;
&lt;th class="head"&gt;&lt;p&gt;Description&lt;/p&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;p&gt;&lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;/document-storage/json/2/docs&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;List documents&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;p&gt;&lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;/document-storage/json/2/upload/request&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;Prepare upload&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;p&gt;&lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;/document-storage/json/2/upload/update-status&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;Update metadata&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;p&gt;&lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;/document-storage/json/2/delete&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;Delete a document&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The API only works on the metadata.
The content (Notebooks, PDF documents, EPUB files) is handled separately by
down- or uploading a ZIP archive.&lt;/p&gt;
&lt;p&gt;The metadata is represented by the following JSON document:&lt;/p&gt;
&lt;pre class="code json"&gt;&lt;a name="rest_code_06b7acaf413f4cec946d07efd7ccc627-1"&gt;&lt;/a&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;a name="rest_code_06b7acaf413f4cec946d07efd7ccc627-2"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;"ID"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"0631045c-e3a9-45a0-8446-abcdef012345"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_06b7acaf413f4cec946d07efd7ccc627-3"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_06b7acaf413f4cec946d07efd7ccc627-4"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;"Message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_06b7acaf413f4cec946d07efd7ccc627-5"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;"Success"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_06b7acaf413f4cec946d07efd7ccc627-6"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;"BlobURLGet"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"https://storage.googleapis.com/remarkable-production-document-storage/...[snip]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_06b7acaf413f4cec946d07efd7ccc627-7"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;"BlobURLGetExpires"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"2020-12-20T07:07:56.628298857Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_06b7acaf413f4cec946d07efd7ccc627-8"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;"ModifiedClient"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"2020-12-12T22:16:39.539539Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_06b7acaf413f4cec946d07efd7ccc627-9"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;"Type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"DocumentType"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_06b7acaf413f4cec946d07efd7ccc627-10"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;"VissibleName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"My Notebook"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_06b7acaf413f4cec946d07efd7ccc627-11"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;"CurrentPage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_06b7acaf413f4cec946d07efd7ccc627-12"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;"Bookmarked"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_06b7acaf413f4cec946d07efd7ccc627-13"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;"Parent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"ec53580c-3579-4fe7-a096-012345abcdef"&lt;/span&gt;
&lt;a name="rest_code_06b7acaf413f4cec946d07efd7ccc627-14"&gt;&lt;/a&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Note that there is a typo in the &lt;code class="docutils literal"&gt;VissibleName&lt;/code&gt; field.&lt;/p&gt;
&lt;p&gt;Not all of the fields are needed in each request.&lt;/p&gt;
&lt;p&gt;Error messages can bee included in a "200 OK" response
if the &lt;code class="docutils literal"&gt;Success&lt;/code&gt; field is set to &lt;em&gt;false&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre class="code json"&gt;&lt;a name="rest_code_d67e61e94bb14e3895b4d3381a8086b3-1"&gt;&lt;/a&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;a name="rest_code_d67e61e94bb14e3895b4d3381a8086b3-2"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;"ID"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"ec53580c-3579-4fe7-a096-fd1de8011b70"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_d67e61e94bb14e3895b4d3381a8086b3-3"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_d67e61e94bb14e3895b4d3381a8086b3-4"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;"Message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Not found or access denied"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_d67e61e94bb14e3895b4d3381a8086b3-5"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;"Success"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_d67e61e94bb14e3895b4d3381a8086b3-6"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;"BlobURLGet"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_d67e61e94bb14e3895b4d3381a8086b3-7"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;"BlobURLGetExpires"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"0001-01-01T00:00:00Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_d67e61e94bb14e3895b4d3381a8086b3-8"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;"ModifiedClient"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"0001-01-01T00:00:00Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_d67e61e94bb14e3895b4d3381a8086b3-9"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;"Type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_d67e61e94bb14e3895b4d3381a8086b3-10"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;"VissibleName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_d67e61e94bb14e3895b4d3381a8086b3-11"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;"CurrentPage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_d67e61e94bb14e3895b4d3381a8086b3-12"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;"Bookmarked"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_d67e61e94bb14e3895b4d3381a8086b3-13"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;"Parent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;
&lt;a name="rest_code_d67e61e94bb14e3895b4d3381a8086b3-14"&gt;&lt;/a&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;For status codes other than 200 the error message seems to be sent as
plain text.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre class="literal-block"&gt;Serving request failed, Msg: Authorization failed, invalid token: Could not validate jwt token: Token is expired, Origin: Authorization failed, invalid token: Could not validate jwt token: Token is expired, HTTPCode: 401&lt;/pre&gt;
&lt;div class="section" id="list-documents"&gt;
&lt;h4&gt;List Documents&lt;/h4&gt;
&lt;p&gt;The request is a &lt;code class="docutils literal"&gt;GET&lt;/code&gt; on the list endpoint:&lt;/p&gt;
&lt;pre class="literal-block"&gt;GET /document-storage/json/2/docs&lt;/pre&gt;
&lt;p&gt;It returns an array of metadata entries:&lt;/p&gt;
&lt;pre class="code json"&gt;&lt;a name="rest_code_9d2030aa2c87456f993bea2aaf1ee513-1"&gt;&lt;/a&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;a name="rest_code_9d2030aa2c87456f993bea2aaf1ee513-2"&gt;&lt;/a&gt;    &lt;span class="p"&gt;{&lt;/span&gt;
&lt;a name="rest_code_9d2030aa2c87456f993bea2aaf1ee513-3"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;"ID"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"ec53580c-3579-4fe7-a096-fd1de8011b70"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_9d2030aa2c87456f993bea2aaf1ee513-4"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_9d2030aa2c87456f993bea2aaf1ee513-5"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;"Message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_9d2030aa2c87456f993bea2aaf1ee513-6"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;"Success"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_9d2030aa2c87456f993bea2aaf1ee513-7"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;"BlobURLGet"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_9d2030aa2c87456f993bea2aaf1ee513-8"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;"BlobURLGetExpires"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"0001-01-01T00:00:00Z"&lt;/span&gt;
&lt;a name="rest_code_9d2030aa2c87456f993bea2aaf1ee513-9"&gt;&lt;/a&gt;        &lt;span class="err"&gt;...&lt;/span&gt;
&lt;a name="rest_code_9d2030aa2c87456f993bea2aaf1ee513-10"&gt;&lt;/a&gt;    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;a name="rest_code_9d2030aa2c87456f993bea2aaf1ee513-11"&gt;&lt;/a&gt;    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;a name="rest_code_9d2030aa2c87456f993bea2aaf1ee513-12"&gt;&lt;/a&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;The &lt;code class="docutils literal"&gt;BlobURLGet&lt;/code&gt; field is always empty when requesting the list.
Other than that, the documents are complete.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="fetch-a-single-item"&gt;
&lt;h4&gt;Fetch a Single Item&lt;/h4&gt;
&lt;p&gt;There is no actual endpoint to request a single item.
Instead, one fetches the list with a filter on an item's ID:&lt;/p&gt;
&lt;pre class="literal-block"&gt;GET /document-storage/json/2/docs?doc=abcdef012345&amp;amp;withBlob=true&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Parameters&lt;/strong&gt; are:&lt;/p&gt;
&lt;table&gt;
&lt;colgroup&gt;
&lt;col style="width: 14%"&gt;
&lt;col style="width: 86%"&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th class="head"&gt;&lt;p&gt;Name&lt;/p&gt;&lt;/th&gt;
&lt;th class="head"&gt;&lt;p&gt;Description&lt;/p&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;p&gt;doc&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;The ID of the requested item (folder or notebook)&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;p&gt;withBlob&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;if present, the BlobURLGet field be set&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The blob URL is used to retrieve a ZIP archive with the actual content.
The archive contains all files that can be found on the tablet's file system:&lt;/p&gt;
&lt;pre class="literal-block"&gt;&amp;lt;archive&amp;gt;
├── 04387b11-4fe3-41c5-9854-e0a5cbf463d5
│   ├── 2cb61608-9092-4c22-876c-b7ce0970ef4e-metadata.json
│   ├── 2cb61608-9092-4c22-876c-b7ce0970ef4e.rm
│   └── a370ab54-0e0f-4558-94d1-63c74d641c40.rm
├── 04387b11-4fe3-41c5-9854-e0a5cbf463d5.content
├── 04387b11-4fe3-41c5-9854-e0a5cbf463d5.lock
├── 04387b11-4fe3-41c5-9854-e0a5cbf463d5.metadata
├── 04387b11-4fe3-41c5-9854-e0a5cbf463d5.pagedata
└── 04387b11-4fe3-41c5-9854-e0a5cbf463d5.thumbnails
    ├── 2cb61608-9092-4c22-876c-b7ce0970ef4e.jpg
    └── a370ab54-0e0f-4558-94d1-63c74d641c40.jpg&lt;/pre&gt;
&lt;p&gt;The file system layout is described in the &lt;a class="reference external" href="https://remarkablewiki.com/tech/filesystem"&gt;unofficial reMarkable wiki&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/remarkable-cloud-api/#id5" id="id7"&gt;2&lt;/a&gt;
in detail.&lt;/p&gt;
&lt;p&gt;Authentication details for this call are already part of the URL
and no additional authentication is needed.&lt;/p&gt;
&lt;p&gt;The service will send error messages in XML format:&lt;/p&gt;
&lt;pre class="code xml"&gt;&lt;a name="rest_code_095bea4cdfec46e9b0e6a60fe3caa240-1"&gt;&lt;/a&gt;&lt;span class="cp"&gt;&amp;lt;?xml version='1.0' encoding='UTF-8'?&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_095bea4cdfec46e9b0e6a60fe3caa240-2"&gt;&lt;/a&gt;&lt;span class="nt"&gt;&amp;lt;Error&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_095bea4cdfec46e9b0e6a60fe3caa240-3"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;&amp;lt;Code&amp;gt;&lt;/span&gt;AuthenticationRequired&lt;span class="nt"&gt;&amp;lt;/Code&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_095bea4cdfec46e9b0e6a60fe3caa240-4"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;&amp;lt;Message&amp;gt;&lt;/span&gt;Authentication required.&lt;span class="nt"&gt;&amp;lt;/Message&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_095bea4cdfec46e9b0e6a60fe3caa240-5"&gt;&lt;/a&gt;&lt;span class="nt"&gt;&amp;lt;/Error&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="section" id="update-metadata"&gt;
&lt;h4&gt;Update Metadata&lt;/h4&gt;
&lt;p&gt;To update the metadata for an item, send a &lt;code class="docutils literal"&gt;PUT&lt;/code&gt; request to the update-status
endpoint:&lt;/p&gt;
&lt;pre class="literal-block"&gt;PUT /document-storage/json/2/upload/update-status&lt;/pre&gt;
&lt;p&gt;The request must include a JSON document with at least the following fields:&lt;/p&gt;
&lt;pre class="code json"&gt;&lt;a name="rest_code_cc34828ede054b56921d52d854b2293c-1"&gt;&lt;/a&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;a name="rest_code_cc34828ede054b56921d52d854b2293c-2"&gt;&lt;/a&gt;    &lt;span class="p"&gt;{&lt;/span&gt;
&lt;a name="rest_code_cc34828ede054b56921d52d854b2293c-3"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;"ID"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"0631045c-e3a9-45a0-8446-abcdef012345"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_cc34828ede054b56921d52d854b2293c-4"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_cc34828ede054b56921d52d854b2293c-5"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;"ModifiedClient"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"2020-12-12T22:16:39.539539Z"&lt;/span&gt;
&lt;a name="rest_code_cc34828ede054b56921d52d854b2293c-6"&gt;&lt;/a&gt;    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;a name="rest_code_cc34828ede054b56921d52d854b2293c-7"&gt;&lt;/a&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;&lt;em&gt;Note that we always need to send an array, although we update a single document.&lt;/em&gt;&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;The &lt;code class="docutils literal"&gt;Version&lt;/code&gt; must be incremented by one,
based on the last version that you have seen.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;code class="docutils literal"&gt;ModifiedClient&lt;/code&gt; field should be set to the current time (UTC).
Requests will still be processed even if the timestamp is not recent.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Additionally, set the fields you want to change:&lt;/p&gt;
&lt;table&gt;
&lt;colgroup&gt;
&lt;col style="width: 26%"&gt;
&lt;col style="width: 74%"&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th class="head"&gt;&lt;p&gt;Field&lt;/p&gt;&lt;/th&gt;
&lt;th class="head"&gt;&lt;p&gt;Description&lt;/p&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;p&gt;&lt;code class="docutils literal"&gt;VissibleName&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;&lt;strong&gt;Rename&lt;/strong&gt; an item&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;p&gt;&lt;code class="docutils literal"&gt;Parent&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;&lt;strong&gt;Move&lt;/strong&gt; an item to a different parent folder&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;p&gt;&lt;code class="docutils literal"&gt;Bookmarked&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;&lt;strong&gt;Bookmark&lt;/strong&gt; an item (or remove a bookmark&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;When moving a &lt;em&gt;CollectionType&lt;/em&gt; item, all its children are also moved.
Specify an empty &lt;cite&gt;Parent&lt;/cite&gt; to move an item to the root folder.&lt;/p&gt;
&lt;div class="admonition warning"&gt;
&lt;p class="admonition-title"&gt;Warning&lt;/p&gt;
&lt;p&gt;The API does not seem to validate the parent ID.
Setting the parent to a non-existing ID will make the document appear in
the root folder.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;This should return a list of items with the updated status.&lt;/p&gt;
&lt;p&gt;On success:&lt;/p&gt;
&lt;pre class="code json"&gt;&lt;a name="rest_code_bfecd583921d4207b38e7d819b202db7-1"&gt;&lt;/a&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;a name="rest_code_bfecd583921d4207b38e7d819b202db7-2"&gt;&lt;/a&gt;    &lt;span class="p"&gt;{&lt;/span&gt;
&lt;a name="rest_code_bfecd583921d4207b38e7d819b202db7-3"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;"ID"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"25e3a0ce-080a-4389-be2a-f6aa45ce0207"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_bfecd583921d4207b38e7d819b202db7-4"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;48&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_bfecd583921d4207b38e7d819b202db7-5"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;"Message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_bfecd583921d4207b38e7d819b202db7-6"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;"Success"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;
&lt;a name="rest_code_bfecd583921d4207b38e7d819b202db7-7"&gt;&lt;/a&gt;    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;a name="rest_code_bfecd583921d4207b38e7d819b202db7-8"&gt;&lt;/a&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;On error:&lt;/p&gt;
&lt;pre class="code json"&gt;&lt;a name="rest_code_2670a4d5762441448ca84a6808768968-1"&gt;&lt;/a&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;a name="rest_code_2670a4d5762441448ca84a6808768968-2"&gt;&lt;/a&gt;    &lt;span class="p"&gt;{&lt;/span&gt;
&lt;a name="rest_code_2670a4d5762441448ca84a6808768968-3"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;"ID"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"25e3a0ce-080a-4389-be2a-f6aa45ce0207"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_2670a4d5762441448ca84a6808768968-4"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;48&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_2670a4d5762441448ca84a6808768968-5"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;"Message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Version on server is not -1 of what you supplied: Server: 48, Client req: 48"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_2670a4d5762441448ca84a6808768968-6"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;"Success"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;a name="rest_code_2670a4d5762441448ca84a6808768968-7"&gt;&lt;/a&gt;    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;a name="rest_code_2670a4d5762441448ca84a6808768968-8"&gt;&lt;/a&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="section" id="upload-a-document"&gt;
&lt;h4&gt;Upload a Document&lt;/h4&gt;
&lt;p&gt;Uploading documents like PDF or EPUB or notebooks is a multi-step process:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;Create an "Upload Request"
(returns &lt;code class="docutils literal"&gt;BlobURLPut&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use the &lt;code class="docutils literal"&gt;BlobURLPut&lt;/code&gt; to upload a zip file with the content.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Make another request to Update (rather: set) the metadata for the item.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="figure"&gt;
&lt;img alt="/images/remarkable-api-upload.mermaid.svg" src="https://akeil.de/images/remarkable-api-upload.mermaid.svg"&gt;
&lt;p class="caption"&gt;Sequence diagram with the steps required to upload a document.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;If the upload for the zipped content fails, the item is not created
(it cannot be deleted and apparently does not have to be deleted).&lt;/p&gt;
&lt;p&gt;The first request is made against the upload request endpoint:&lt;/p&gt;
&lt;pre class="literal-block"&gt;PUT /document-storage/json/2/upload/request&lt;/pre&gt;
&lt;p&gt;With some basic data as input:&lt;/p&gt;
&lt;pre class="code json"&gt;&lt;a name="rest_code_81457d5d213e416984b02fc63c803047-1"&gt;&lt;/a&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;a name="rest_code_81457d5d213e416984b02fc63c803047-2"&gt;&lt;/a&gt;    &lt;span class="p"&gt;{&lt;/span&gt;
&lt;a name="rest_code_81457d5d213e416984b02fc63c803047-3"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;"ID"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"923812ec-ae98-40c3-bc59-e7ef065537ff"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_81457d5d213e416984b02fc63c803047-4"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_81457d5d213e416984b02fc63c803047-5"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;"ModifiedClient"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"2020-12-20T12:00:34.814814Z"&lt;/span&gt;
&lt;a name="rest_code_81457d5d213e416984b02fc63c803047-6"&gt;&lt;/a&gt;    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;a name="rest_code_81457d5d213e416984b02fc63c803047-7"&gt;&lt;/a&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;The response (if successful) contains the upload URL:&lt;/p&gt;
&lt;pre class="code json"&gt;&lt;a name="rest_code_0d7f555b25f04e9b957277ab73ca32e0-1"&gt;&lt;/a&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;a name="rest_code_0d7f555b25f04e9b957277ab73ca32e0-2"&gt;&lt;/a&gt;    &lt;span class="p"&gt;{&lt;/span&gt;
&lt;a name="rest_code_0d7f555b25f04e9b957277ab73ca32e0-3"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;"ID"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"923812ec-ae98-40c3-bc59-e7ef065537ff"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_0d7f555b25f04e9b957277ab73ca32e0-4"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_0d7f555b25f04e9b957277ab73ca32e0-5"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;"Message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_0d7f555b25f04e9b957277ab73ca32e0-6"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;"Success"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_0d7f555b25f04e9b957277ab73ca32e0-7"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;"BlobURLPut"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"https://storage.googleapis.com/remarkable-production-document-storage/...[snip]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_0d7f555b25f04e9b957277ab73ca32e0-8"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;"BlobURLPutExpires"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"2020-12-20T12:20:48.434022276Z"&lt;/span&gt;
&lt;a name="rest_code_0d7f555b25f04e9b957277ab73ca32e0-9"&gt;&lt;/a&gt;    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;a name="rest_code_0d7f555b25f04e9b957277ab73ca32e0-10"&gt;&lt;/a&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Another request is made to upload content:&lt;/p&gt;
&lt;pre class="literal-block"&gt;PUT {{BlobURLPut}}&lt;/pre&gt;
&lt;p&gt;The content is a Zip archive with the remarkable file format.&lt;/p&gt;
&lt;p&gt;And a &lt;em&gt;third&lt;/em&gt; request against the update endpoint to set the metadata:&lt;/p&gt;
&lt;pre class="literal-block"&gt;PUT /document-storage/json/2/upload/update-status&lt;/pre&gt;
&lt;pre class="code json"&gt;&lt;a name="rest_code_cccdec349dc24a969af8b30b972fa9b6-1"&gt;&lt;/a&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;a name="rest_code_cccdec349dc24a969af8b30b972fa9b6-2"&gt;&lt;/a&gt;    &lt;span class="p"&gt;{&lt;/span&gt;
&lt;a name="rest_code_cccdec349dc24a969af8b30b972fa9b6-3"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;"ID"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"923812ec-ae98-40c3-bc59-e7ef065537ff"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_cccdec349dc24a969af8b30b972fa9b6-4"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_cccdec349dc24a969af8b30b972fa9b6-5"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;"ModifiedClient"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"2020-12-20T12:00:34.814814Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_cccdec349dc24a969af8b30b972fa9b6-6"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;"Type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"DocumentType"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_cccdec349dc24a969af8b30b972fa9b6-7"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;"VissibleName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"My Document"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_cccdec349dc24a969af8b30b972fa9b6-8"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;"Bookmarked"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_cccdec349dc24a969af8b30b972fa9b6-9"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;"Parent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"f86e2e28-234d-485d-ab34-fe1184571a92"&lt;/span&gt;
&lt;a name="rest_code_cccdec349dc24a969af8b30b972fa9b6-10"&gt;&lt;/a&gt;    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;a name="rest_code_cccdec349dc24a969af8b30b972fa9b6-11"&gt;&lt;/a&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;div class="admonition note"&gt;
&lt;p class="admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;The &lt;code class="docutils literal"&gt;Version&lt;/code&gt; field is set to &lt;strong&gt;1&lt;/strong&gt;, as this is a new item.
The first request which created the upload URL does not create a
"first version".&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="delete-an-item"&gt;
&lt;h4&gt;Delete an Item&lt;/h4&gt;
&lt;p&gt;To delete an item, send a PUT request with the metadata of the item to be
deleted:&lt;/p&gt;
&lt;pre class="literal-block"&gt;PUT /document-storage/json/2/delete&lt;/pre&gt;
&lt;p&gt;The request body needs the basic metadata of the document:&lt;/p&gt;
&lt;pre class="code json"&gt;&lt;a name="rest_code_3550d07fe4474920a71845bb5493546d-1"&gt;&lt;/a&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;a name="rest_code_3550d07fe4474920a71845bb5493546d-2"&gt;&lt;/a&gt;    &lt;span class="p"&gt;{&lt;/span&gt;
&lt;a name="rest_code_3550d07fe4474920a71845bb5493546d-3"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;"ID"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"81c58d3d-a550-4079-85c8-b581d7673390"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_3550d07fe4474920a71845bb5493546d-4"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_3550d07fe4474920a71845bb5493546d-5"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;"ModifiedClient"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"2020-12-20T12:00:34.814814Z"&lt;/span&gt;
&lt;a name="rest_code_3550d07fe4474920a71845bb5493546d-6"&gt;&lt;/a&gt;    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;a name="rest_code_3550d07fe4474920a71845bb5493546d-7"&gt;&lt;/a&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Deleting an item will set its &lt;code class="docutils literal"&gt;parent&lt;/code&gt; to the special value &lt;code class="docutils literal"&gt;trash&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you delete a folder, &lt;em&gt;only&lt;/em&gt; that folder's parent will be changed to &lt;code class="docutils literal"&gt;trash&lt;/code&gt;.
Any document contained inside the folder remains unchanged.
It will become "invisible" though, as its parent element is no more accessible.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id2"&gt;
&lt;h3&gt;Notifications&lt;/h3&gt;
&lt;p&gt;The notifications &lt;strong&gt;host&lt;/strong&gt; is obtained via the &lt;em&gt;Discovery&lt;/em&gt; URL.&lt;/p&gt;
&lt;p&gt;The endpoint is
&lt;code class="docutils literal"&gt;/notifications/ws/json/1&lt;/code&gt;
(found in this &lt;a class="reference external" href="https://github.com/reMarkable/ws-stresser/blob/master/config.ini"&gt;reMarkable github repository&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/remarkable-cloud-api/#id14" id="id15"&gt;6&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;The complete URL might look like this:&lt;/p&gt;
&lt;pre class="literal-block"&gt;wss://299t-notifications-production.cloud.remarkable.engineering/notifications/ws/json/1&lt;/pre&gt;
&lt;p&gt;The connection must be authenticated with a &lt;em&gt;User Token&lt;/em&gt;
in the &lt;cite&gt;Authentication: Bearer&lt;/cite&gt; header.&lt;/p&gt;
&lt;p&gt;Assumption: The notifications are one-way,
meaning clients will will only receive and never send messages.&lt;/p&gt;
&lt;div class="section" id="messages"&gt;
&lt;h4&gt;Messages&lt;/h4&gt;
&lt;p&gt;Messages are JSON objects an look like this:&lt;/p&gt;
&lt;pre class="code json"&gt;&lt;a name="rest_code_cf5eda8716604f7b88a3c845d2fff515-1"&gt;&lt;/a&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;a name="rest_code_cf5eda8716604f7b88a3c845d2fff515-2"&gt;&lt;/a&gt;  &lt;span class="nt"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;a name="rest_code_cf5eda8716604f7b88a3c845d2fff515-3"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;"attributes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;a name="rest_code_cf5eda8716604f7b88a3c845d2fff515-4"&gt;&lt;/a&gt;      &lt;span class="nt"&gt;"auth0UserID"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"auth0|123456abcdef130000000000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_cf5eda8716604f7b88a3c845d2fff515-5"&gt;&lt;/a&gt;      &lt;span class="nt"&gt;"bookmarked"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"false"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_cf5eda8716604f7b88a3c845d2fff515-6"&gt;&lt;/a&gt;      &lt;span class="nt"&gt;"event"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"DocAdded"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_cf5eda8716604f7b88a3c845d2fff515-7"&gt;&lt;/a&gt;      &lt;span class="nt"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"b083f079-c45e-4b1f-81ea-32c36a672142"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_cf5eda8716604f7b88a3c845d2fff515-8"&gt;&lt;/a&gt;      &lt;span class="nt"&gt;"parent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"106ec061-d552-417b-a09c-e207cf87f597"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_cf5eda8716604f7b88a3c845d2fff515-9"&gt;&lt;/a&gt;      &lt;span class="nt"&gt;"sourceDeviceDesc"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"remarkable"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_cf5eda8716604f7b88a3c845d2fff515-10"&gt;&lt;/a&gt;      &lt;span class="nt"&gt;"sourceDeviceID"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"RM110-123-12345"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_cf5eda8716604f7b88a3c845d2fff515-11"&gt;&lt;/a&gt;      &lt;span class="nt"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"DocumentType"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_cf5eda8716604f7b88a3c845d2fff515-12"&gt;&lt;/a&gt;      &lt;span class="nt"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_cf5eda8716604f7b88a3c845d2fff515-13"&gt;&lt;/a&gt;      &lt;span class="nt"&gt;"vissibleName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"My Notebook"&lt;/span&gt;
&lt;a name="rest_code_cf5eda8716604f7b88a3c845d2fff515-14"&gt;&lt;/a&gt;    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;a name="rest_code_cf5eda8716604f7b88a3c845d2fff515-15"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;"messageId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"1804732942123456"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_cf5eda8716604f7b88a3c845d2fff515-16"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;"message_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"1804732942123456"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_cf5eda8716604f7b88a3c845d2fff515-17"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;"publishTime"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"2020-12-21T10:09:59.016Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a name="rest_code_cf5eda8716604f7b88a3c845d2fff515-18"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;"publish_time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"2020-12-21T10:09:59.016Z"&lt;/span&gt;
&lt;a name="rest_code_cf5eda8716604f7b88a3c845d2fff515-19"&gt;&lt;/a&gt;  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;a name="rest_code_cf5eda8716604f7b88a3c845d2fff515-20"&gt;&lt;/a&gt;  &lt;span class="nt"&gt;"subscription"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"projects/remarkable-production/subscriptions/sub-299t-notifications-production"&lt;/span&gt;
&lt;a name="rest_code_cf5eda8716604f7b88a3c845d2fff515-21"&gt;&lt;/a&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;The &lt;code class="docutils literal"&gt;message&lt;/code&gt; object consists of some metadata like &lt;code class="docutils literal"&gt;messageId&lt;/code&gt;
and &lt;code class="docutils literal"&gt;publishTime&lt;/code&gt;.
The actual message content is contained in the &lt;code class="docutils literal"&gt;attributes&lt;/code&gt; property:&lt;/p&gt;
&lt;table&gt;
&lt;colgroup&gt;
&lt;col style="width: 14%"&gt;
&lt;col style="width: 5%"&gt;
&lt;col style="width: 81%"&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th class="head"&gt;&lt;p&gt;Name&lt;/p&gt;&lt;/th&gt;
&lt;th class="head"&gt;&lt;p&gt;Type&lt;/p&gt;&lt;/th&gt;
&lt;th class="head"&gt;&lt;p&gt;Description&lt;/p&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;p&gt;auth0UserID&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;string&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;Seems to be the same as the one used in discovery; not sure what it means or why it is there&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;p&gt;bookmarked&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;bool&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;&lt;em&gt;true&lt;/em&gt; if the item is bookmarked&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;p&gt;event&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;string&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;One of "DocAdded" or "DocDeleted"&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;p&gt;id&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;string&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;The UUID of the item&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;p&gt;parent&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;string&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;The UUID of the parent item&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;p&gt;sourceDeviceDesc&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;string&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;These seems to be the device description from the registration process&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;p&gt;sourceDeviceID&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;string&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;could be the ID of the device used during registration&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;p&gt;type&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;string&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;"DocumentType" or "CollectionType"&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;p&gt;version&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;int&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;The change version of the item&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;p&gt;vissibleName&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;string&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;The name of the item, typo included&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Note that all attributes are encoded as strings.
One needs to parse the actual data type from that string.&lt;/p&gt;
&lt;p&gt;The &lt;code class="docutils literal"&gt;sourceDeviceID&lt;/code&gt; allows us to filter notifications caused by our own actions.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="events"&gt;
&lt;h4&gt;Events&lt;/h4&gt;
&lt;p&gt;We will receive notifications on the following events:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;New notebook created&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Notebook deleted&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Notebook changed (bookmarked, renamed or moved)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Folder created&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Folder deleted&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Folder Changed (bookmarked, renamed or moved)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Creating items &lt;em&gt;and&lt;/em&gt; updating items generates the same type of notification:
&lt;code class="docutils literal"&gt;DocAdded&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;When an item is &lt;strong&gt;deleted&lt;/strong&gt;, we will receive a notification with the
&lt;code class="docutils literal"&gt;DocDeleted&lt;/code&gt; type.&lt;/p&gt;
&lt;p&gt;Note that the UI on the tablet does not actually delete items but moves them
to a "Trash" folder.
In that case the parent ID is set to the special value &lt;code class="docutils literal"&gt;trash&lt;/code&gt;
and we receive a &lt;code class="docutils literal"&gt;DocAdded&lt;/code&gt; event.&lt;/p&gt;
&lt;hr class="docutils"&gt;
&lt;dl class="footnote brackets"&gt;
&lt;dt class="label" id="id3"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/remarkable-cloud-api/#id4"&gt;1&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="https://remarkable.com/"&gt;https://remarkable.com/&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id5"&gt;&lt;span class="brackets"&gt;2&lt;/span&gt;&lt;span class="fn-backref"&gt;(&lt;a href="https://akeil.de/posts/remarkable-cloud-api/#id6"&gt;1&lt;/a&gt;,&lt;a href="https://akeil.de/posts/remarkable-cloud-api/#id7"&gt;2&lt;/a&gt;)&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="https://remarkablewiki.com/tech/filesystem"&gt;https://remarkablewiki.com/tech/filesystem&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id8"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/remarkable-cloud-api/#id9"&gt;3&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="https://github.com/splitbrain/ReMarkableAPI"&gt;https://github.com/splitbrain/ReMarkableAPI&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id10"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/remarkable-cloud-api/#id11"&gt;4&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="https://github.com/juruen/rmapi"&gt;https://github.com/juruen/rmapi&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id12"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/remarkable-cloud-api/#id13"&gt;5&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="https://github.com/akeil/rmtool"&gt;https://github.com/akeil/rmtool&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id14"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/remarkable-cloud-api/#id15"&gt;6&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="https://github.com/reMarkable/ws-stresser/blob/master/config.ini"&gt;https://github.com/reMarkable/ws-stresser/blob/master/config.ini&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description><category>missing-manual</category><category>remarkable</category><guid>https://akeil.de/posts/remarkable-cloud-api/</guid><pubDate>Sat, 02 Jan 2021 19:33:13 GMT</pubDate></item><item><title>InfluxDB for TinyCore Linux</title><link>https://akeil.de/posts/influxdb-for-tinycore-linux/</link><dc:creator>Alexander Keil</dc:creator><description>&lt;div class="section" id="influxdb-for-tinycore-linux-arm"&gt;
&lt;h2&gt;InfluxDB for TinyCore Linux (ARM)&lt;/h2&gt;
&lt;dl class="field-list simple"&gt;
&lt;dt&gt;author&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;akeil&lt;/p&gt;
&lt;/dd&gt;
&lt;dt&gt;date&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;2017-08-13&lt;/p&gt;
&lt;/dd&gt;
&lt;dt&gt;version&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;1&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;This post shows how to set up &lt;a class="reference external" href="https://www.influxdata.com/time-series-platform/influxdb/"&gt;InfluxDB&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/influxdb-for-tinycore-linux/#id1" id="id2"&gt;1&lt;/a&gt; on a RaspberryPi running &lt;a class="reference external" href="http://tinycorelinux.net/"&gt;TinyCore Linux&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/influxdb-for-tinycore-linux/#id3" id="id4"&gt;2&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is intended as part of a setup for home automation with where InfluxDB is used
as a storage backend for statistics.&lt;/p&gt;
&lt;!-- TEASER_END --&gt;
&lt;div class="section" id="tiny-core-extension"&gt;
&lt;h3&gt;Tiny Core Extension&lt;/h3&gt;
&lt;p&gt;Before we can install InfluxDB, we need to create a &lt;a class="reference external" href="http://wiki.tinycorelinux.net/wiki:creating_extensions"&gt;TinyCore Extension&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/influxdb-for-tinycore-linux/#id5" id="id6"&gt;3&lt;/a&gt; for it.&lt;/p&gt;
&lt;p&gt;Download and extract the prebuilt binary from the &lt;a class="reference external" href="https://portal.influxdata.com/downloads#influxdb"&gt;InfluxDB Downloads&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/influxdb-for-tinycore-linux/#id7" id="id8"&gt;4&lt;/a&gt; site:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_05fcbb9851974a82bfcbc5eee94554b8-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; wget https://dl.influxdata.com/influxdb/releases/influxdb-1.3.2_linux_armhf.tar.gz
&lt;a name="rest_code_05fcbb9851974a82bfcbc5eee94554b8-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; tar -xzf influxdb-1.3.2_linux_armhf.tar.gz
&lt;/pre&gt;&lt;div class="admonition note"&gt;
&lt;p class="admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;The download URL is different for each version.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The extracted archive creates a directory tree like this:&lt;/p&gt;
&lt;pre class="literal-block"&gt;etc/
    influxdb/           # default config
    logrotate.d/
usr/
    bin/                # binaries
    lib/
    share/
var/
    lib/
        influxdb/
    log/
        influxdb/&lt;/pre&gt;
&lt;p&gt;Only a minimal selection of files goes into the TinyCore extension.
Create a temporary directory (&lt;code class="docutils literal"&gt;/tmp/influxdb/usr/local&lt;/code&gt;)
and place selected files there.
The complete destination directory looks like this:&lt;/p&gt;
&lt;pre class="literal-block"&gt;/tmp/influxdb/
    usr/
        local/
            bin/            # binaries
            etc/            # sample config
            tce.installed/  # post-install script&lt;/pre&gt;
&lt;div class="section" id="binaries"&gt;
&lt;h4&gt;Binaries&lt;/h4&gt;
&lt;p&gt;From the source &lt;code class="docutils literal"&gt;usr/&lt;/code&gt; directory, only the binaries are used. This omits
the man pages (under &lt;code class="docutils literal"&gt;share/&lt;/code&gt;) and start scripts (under &lt;code class="docutils literal"&gt;lib/&lt;/code&gt;).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="configuration-file"&gt;
&lt;h4&gt;Configuration File&lt;/h4&gt;
&lt;p&gt;The InfluxDB distribution contains a default configuration file which is
the starting point for a custom configuration.&lt;/p&gt;
&lt;p&gt;Reduce cache sizes to play nice with the available memory (see
&lt;a class="reference external" href="https://docs.influxdata.com/influxdb/v1.3/administration/config/#data"&gt;InfluxDB docs&lt;/a&gt;
):&lt;/p&gt;
&lt;pre class="code ini"&gt;&lt;a name="rest_code_50607e91b9f0470984c88ecbb39c07c2-1"&gt;&lt;/a&gt;&lt;span class="k"&gt;[data]&lt;/span&gt;
&lt;a name="rest_code_50607e91b9f0470984c88ecbb39c07c2-2"&gt;&lt;/a&gt;&lt;span class="na"&gt;cache-max-memory-size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;20971520&lt;/span&gt;
&lt;a name="rest_code_50607e91b9f0470984c88ecbb39c07c2-3"&gt;&lt;/a&gt;&lt;span class="na"&gt;cache-snapshot-memory-size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;10485760&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Disable usage statistics:&lt;/p&gt;
&lt;pre class="code ini"&gt;&lt;a name="rest_code_05ece04a73294b50ad05799b4ecf06af-1"&gt;&lt;/a&gt;&lt;span class="na"&gt;reporting-disabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;true&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Place the configuration file in the temporary &lt;code class="docutils literal"&gt;etc&lt;/code&gt; directory,
naming it &lt;code class="docutils literal"&gt;influxdb.sample.conf&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="post-install-script"&gt;
&lt;h4&gt;Post-Install Script&lt;/h4&gt;
&lt;p&gt;TinyCore supports a &lt;em&gt;tce.installed&lt;/em&gt; script which is run after the
extension was loaded. Our post-install script will&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;if no config file is present, copy the sample config
to the default location for the config file.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;create the &lt;code class="docutils literal"&gt;influxdb&lt;/code&gt; user.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;create the default data directories with correct permissions.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Create a &lt;em&gt;tce.installed&lt;/em&gt; script like this:&lt;/p&gt;
&lt;pre class="code bash"&gt;&lt;a name="rest_code_8b454fbb43f747ed8d882d78615b4e68-1"&gt;&lt;/a&gt;&lt;span class="ch"&gt;#!/bin/sh&lt;/span&gt;
&lt;a name="rest_code_8b454fbb43f747ed8d882d78615b4e68-2"&gt;&lt;/a&gt;
&lt;a name="rest_code_8b454fbb43f747ed8d882d78615b4e68-3"&gt;&lt;/a&gt;&lt;span class="nv"&gt;CFG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/local/etc/influxdb/influxdb.conf
&lt;a name="rest_code_8b454fbb43f747ed8d882d78615b4e68-4"&gt;&lt;/a&gt;&lt;span class="nv"&gt;SAMPLE_CFG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/local/etc/influxdb/influxdb.sample.conf
&lt;a name="rest_code_8b454fbb43f747ed8d882d78615b4e68-5"&gt;&lt;/a&gt;&lt;span class="nv"&gt;DATADIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/var/lib/influxdb
&lt;a name="rest_code_8b454fbb43f747ed8d882d78615b4e68-6"&gt;&lt;/a&gt;&lt;span class="nv"&gt;USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;influxdb
&lt;a name="rest_code_8b454fbb43f747ed8d882d78615b4e68-7"&gt;&lt;/a&gt;
&lt;a name="rest_code_8b454fbb43f747ed8d882d78615b4e68-8"&gt;&lt;/a&gt;&lt;span class="c1"&gt;# if no config exists, use the sample config&lt;/span&gt;
&lt;a name="rest_code_8b454fbb43f747ed8d882d78615b4e68-9"&gt;&lt;/a&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; ! -e &lt;span class="nv"&gt;$CFG&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;a name="rest_code_8b454fbb43f747ed8d882d78615b4e68-10"&gt;&lt;/a&gt;    cp &lt;span class="nv"&gt;$SAMPLE_CFG&lt;/span&gt; &lt;span class="nv"&gt;$CFG&lt;/span&gt;
&lt;a name="rest_code_8b454fbb43f747ed8d882d78615b4e68-11"&gt;&lt;/a&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;a name="rest_code_8b454fbb43f747ed8d882d78615b4e68-12"&gt;&lt;/a&gt;
&lt;a name="rest_code_8b454fbb43f747ed8d882d78615b4e68-13"&gt;&lt;/a&gt;&lt;span class="c1"&gt;# create the influxdb user&lt;/span&gt;
&lt;a name="rest_code_8b454fbb43f747ed8d882d78615b4e68-14"&gt;&lt;/a&gt;grep -q &lt;span class="nv"&gt;$USER&lt;/span&gt; /etc/passwd
&lt;a name="rest_code_8b454fbb43f747ed8d882d78615b4e68-15"&gt;&lt;/a&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt; -ne &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;a name="rest_code_8b454fbb43f747ed8d882d78615b4e68-16"&gt;&lt;/a&gt;    adduser -S -H &lt;span class="nv"&gt;$USER&lt;/span&gt;
&lt;a name="rest_code_8b454fbb43f747ed8d882d78615b4e68-17"&gt;&lt;/a&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;a name="rest_code_8b454fbb43f747ed8d882d78615b4e68-18"&gt;&lt;/a&gt;
&lt;a name="rest_code_8b454fbb43f747ed8d882d78615b4e68-19"&gt;&lt;/a&gt;&lt;span class="c1"&gt;# create the default data directory&lt;/span&gt;
&lt;a name="rest_code_8b454fbb43f747ed8d882d78615b4e68-20"&gt;&lt;/a&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; ! -e &lt;span class="nv"&gt;$DATADIR&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;a name="rest_code_8b454fbb43f747ed8d882d78615b4e68-21"&gt;&lt;/a&gt;    mkdir &lt;span class="nv"&gt;$DATADIR&lt;/span&gt;
&lt;a name="rest_code_8b454fbb43f747ed8d882d78615b4e68-22"&gt;&lt;/a&gt;    chown &lt;span class="nv"&gt;$USER&lt;/span&gt;:nogroup &lt;span class="nv"&gt;$DATADIR&lt;/span&gt;
&lt;a name="rest_code_8b454fbb43f747ed8d882d78615b4e68-23"&gt;&lt;/a&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;The script is added to the temporary installation dir under
&lt;code class="docutils literal"&gt;tce.installed/influxdb&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="package-script"&gt;
&lt;h4&gt;Package Script&lt;/h4&gt;
&lt;p&gt;To make the process easily repeatable for new versions of InfluxDB,
create a &lt;code class="docutils literal"&gt;package.sh&lt;/code&gt; script.
The script includes all steps except downloading the distribution files
for InfluxDB.&lt;/p&gt;
&lt;pre class="code bash"&gt;&lt;a name="rest_code_44e0d2ac4d8a41c3a79d9ed8cbc13b5f-1"&gt;&lt;/a&gt;&lt;span class="ch"&gt;#!/bin/sh&lt;/span&gt;
&lt;a name="rest_code_44e0d2ac4d8a41c3a79d9ed8cbc13b5f-2"&gt;&lt;/a&gt;&lt;span class="nv"&gt;SRC&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;influxdb-1.3.2-1
&lt;a name="rest_code_44e0d2ac4d8a41c3a79d9ed8cbc13b5f-3"&gt;&lt;/a&gt;&lt;span class="nv"&gt;ROOT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/tmp/influxdb/usr/local
&lt;a name="rest_code_44e0d2ac4d8a41c3a79d9ed8cbc13b5f-4"&gt;&lt;/a&gt;
&lt;a name="rest_code_44e0d2ac4d8a41c3a79d9ed8cbc13b5f-5"&gt;&lt;/a&gt;&lt;span class="c1"&gt;# download and install required tools&lt;/span&gt;
&lt;a name="rest_code_44e0d2ac4d8a41c3a79d9ed8cbc13b5f-6"&gt;&lt;/a&gt;tce-load -w squashfs-tools
&lt;a name="rest_code_44e0d2ac4d8a41c3a79d9ed8cbc13b5f-7"&gt;&lt;/a&gt;tce-load -i squashfs-tools
&lt;a name="rest_code_44e0d2ac4d8a41c3a79d9ed8cbc13b5f-8"&gt;&lt;/a&gt;
&lt;a name="rest_code_44e0d2ac4d8a41c3a79d9ed8cbc13b5f-9"&gt;&lt;/a&gt;&lt;span class="c1"&gt;# cleanup&lt;/span&gt;
&lt;a name="rest_code_44e0d2ac4d8a41c3a79d9ed8cbc13b5f-10"&gt;&lt;/a&gt;sudo rm -rf &lt;span class="nv"&gt;$ROOT&lt;/span&gt;
&lt;a name="rest_code_44e0d2ac4d8a41c3a79d9ed8cbc13b5f-11"&gt;&lt;/a&gt;
&lt;a name="rest_code_44e0d2ac4d8a41c3a79d9ed8cbc13b5f-12"&gt;&lt;/a&gt;mkdir -p &lt;span class="nv"&gt;$ROOT&lt;/span&gt;/bin
&lt;a name="rest_code_44e0d2ac4d8a41c3a79d9ed8cbc13b5f-13"&gt;&lt;/a&gt;cp -a &lt;span class="nv"&gt;$SRC&lt;/span&gt;/usr/bin/* &lt;span class="nv"&gt;$ROOT&lt;/span&gt;/bin
&lt;a name="rest_code_44e0d2ac4d8a41c3a79d9ed8cbc13b5f-14"&gt;&lt;/a&gt;
&lt;a name="rest_code_44e0d2ac4d8a41c3a79d9ed8cbc13b5f-15"&gt;&lt;/a&gt;mkdir -p &lt;span class="nv"&gt;$ROOT&lt;/span&gt;/etc/influxdb
&lt;a name="rest_code_44e0d2ac4d8a41c3a79d9ed8cbc13b5f-16"&gt;&lt;/a&gt;cp influxdb.conf &lt;span class="nv"&gt;$ROOT&lt;/span&gt;/etc/influxdb/influxdb.sample.conf
&lt;a name="rest_code_44e0d2ac4d8a41c3a79d9ed8cbc13b5f-17"&gt;&lt;/a&gt;
&lt;a name="rest_code_44e0d2ac4d8a41c3a79d9ed8cbc13b5f-18"&gt;&lt;/a&gt;&lt;span class="c1"&gt;# startup script&lt;/span&gt;
&lt;a name="rest_code_44e0d2ac4d8a41c3a79d9ed8cbc13b5f-19"&gt;&lt;/a&gt;mkdir -p &lt;span class="nv"&gt;$ROOT&lt;/span&gt;/tce.installed
&lt;a name="rest_code_44e0d2ac4d8a41c3a79d9ed8cbc13b5f-20"&gt;&lt;/a&gt;cp tce.installed &lt;span class="nv"&gt;$ROOT&lt;/span&gt;/tce.installed/influxdb
&lt;a name="rest_code_44e0d2ac4d8a41c3a79d9ed8cbc13b5f-21"&gt;&lt;/a&gt;
&lt;a name="rest_code_44e0d2ac4d8a41c3a79d9ed8cbc13b5f-22"&gt;&lt;/a&gt;
&lt;a name="rest_code_44e0d2ac4d8a41c3a79d9ed8cbc13b5f-23"&gt;&lt;/a&gt;&lt;span class="c1"&gt;# default permissions&lt;/span&gt;
&lt;a name="rest_code_44e0d2ac4d8a41c3a79d9ed8cbc13b5f-24"&gt;&lt;/a&gt;sudo chown -R root:root &lt;span class="nv"&gt;$ROOT&lt;/span&gt;
&lt;a name="rest_code_44e0d2ac4d8a41c3a79d9ed8cbc13b5f-25"&gt;&lt;/a&gt;
&lt;a name="rest_code_44e0d2ac4d8a41c3a79d9ed8cbc13b5f-26"&gt;&lt;/a&gt;&lt;span class="c1"&gt;# special perms for startup script&lt;/span&gt;
&lt;a name="rest_code_44e0d2ac4d8a41c3a79d9ed8cbc13b5f-27"&gt;&lt;/a&gt;sudo chown root:staff &lt;span class="nv"&gt;$ROOT&lt;/span&gt;/tce.installed
&lt;a name="rest_code_44e0d2ac4d8a41c3a79d9ed8cbc13b5f-28"&gt;&lt;/a&gt;sudo chown &lt;span class="m"&gt;775&lt;/span&gt; &lt;span class="nv"&gt;$ROOT&lt;/span&gt;/tce.installed
&lt;a name="rest_code_44e0d2ac4d8a41c3a79d9ed8cbc13b5f-29"&gt;&lt;/a&gt;
&lt;a name="rest_code_44e0d2ac4d8a41c3a79d9ed8cbc13b5f-30"&gt;&lt;/a&gt;sudo chown root:staff &lt;span class="nv"&gt;$ROOT&lt;/span&gt;/tce.installed/influxdb
&lt;a name="rest_code_44e0d2ac4d8a41c3a79d9ed8cbc13b5f-31"&gt;&lt;/a&gt;sudo chmod &lt;span class="m"&gt;755&lt;/span&gt; &lt;span class="nv"&gt;$ROOT&lt;/span&gt;/tce.installed/influxdb
&lt;a name="rest_code_44e0d2ac4d8a41c3a79d9ed8cbc13b5f-32"&gt;&lt;/a&gt;
&lt;a name="rest_code_44e0d2ac4d8a41c3a79d9ed8cbc13b5f-33"&gt;&lt;/a&gt;&lt;span class="c1"&gt;# pack and cleanup&lt;/span&gt;
&lt;a name="rest_code_44e0d2ac4d8a41c3a79d9ed8cbc13b5f-34"&gt;&lt;/a&gt;mksquashfs /tmp/influxdb /tmp/influxdb.tcz
&lt;a name="rest_code_44e0d2ac4d8a41c3a79d9ed8cbc13b5f-35"&gt;&lt;/a&gt;sudo rm -rf /tmp/influxdb
&lt;a name="rest_code_44e0d2ac4d8a41c3a79d9ed8cbc13b5f-36"&gt;&lt;/a&gt;
&lt;a name="rest_code_44e0d2ac4d8a41c3a79d9ed8cbc13b5f-37"&gt;&lt;/a&gt;mv /tmp/influxdb.tcz /etc/sysconfig/tcedir/optional/
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="configuration"&gt;
&lt;h3&gt;Configuration&lt;/h3&gt;
&lt;div class="section" id="persistence"&gt;
&lt;h4&gt;Persistence&lt;/h4&gt;
&lt;p&gt;We need to persist two locations, using &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;opt/.filetool.lst&lt;/span&gt;&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="literal-block"&gt;usr/local/etc/influxdb/influxdb.conf
var/lib/influxdb&lt;/pre&gt;
&lt;p&gt;This will include the configuration file and the data directories
in the &lt;code class="docutils literal"&gt;filetool.sh&lt;/code&gt; backup (you still need to remember
to execute this before shutdown). That backup is restored during boot.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="storage"&gt;
&lt;h4&gt;Storage&lt;/h4&gt;
&lt;p&gt;The default storage location is &lt;code class="docutils literal"&gt;/var/lib/influxdb&lt;/code&gt;.
on TinyCore, this resides in memory and can be persisted using &lt;code class="docutils literal"&gt;filetool.sh&lt;/code&gt;.
Since the data files can become quite large, this is not the best option.
Both backup and restore will take a long time and the data will consume
available memory.&lt;/p&gt;
&lt;p&gt;Whether or not to place &lt;em&gt;any&lt;/em&gt; part of the database on volatile storage of course
depends on whether or not (partial) data loss is a problem.
In this case, it is assumed that this is non-critical data and that it is
acceptable if some of the data is lost if the system shuts down unexpectedly.&lt;/p&gt;
&lt;div class="admonition note"&gt;
&lt;p class="admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;Deciding whether to store the InfluxDB on "disk" (SD-Card)
or in memory (with backup/restore on boot)
depends on the &lt;a class="reference external" href="https://docs.influxdata.com/influxdb/v1.3/concepts/storage_engine/"&gt;InfluxDB Storage Engine&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/influxdb-for-tinycore-linux/#id9" id="id10"&gt;5&lt;/a&gt;. there are two important
types of storage:&lt;/p&gt;
&lt;dl class="field-list simple"&gt;
&lt;dt&gt;WAL - Write Ahead Log&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;This is stored under &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;/var/lib/influxdb/wal/&amp;lt;database-name&amp;gt;&lt;/span&gt;&lt;/code&gt;
(the &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;wal-dir=&lt;/span&gt;&lt;/code&gt; directive from config).
New writes go into the WAL, i.e. these files receive
many writes and change often.&lt;/p&gt;
&lt;/dd&gt;
&lt;dt&gt;TSM - Time Structured Merge Tree&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;These are &lt;em&gt;read-only&lt;/em&gt; files which contain the bulk of the data.
They are stored under &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;/var/lib/influxdb/data/&amp;lt;database-name&amp;gt;&lt;/span&gt;&lt;/code&gt;
(the &lt;code class="docutils literal"&gt;dir=&lt;/code&gt; directive from config).
During a process named &lt;em&gt;Compaction&lt;/em&gt;, data from the WAL is periodically
written to TSM files. Also during compaction, existing TSM files are
optimized, i.e. rewritten.&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/div&gt;
&lt;p&gt;The TSM files will probably become too large to keep in memory and are therefore
placed on a separate storage
(in this case, additional storage that is mounted under (&lt;code class="docutils literal"&gt;/mnt/storage&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;The WAL is kept at the default location, in memory.
This has the benefit of faster access times and also reduces writes to the RaspberryPi's
SD-card.
If the WAL is lost, we lose data from the most recent writes.&lt;/p&gt;
&lt;pre class="code ini"&gt;&lt;a name="rest_code_1cd80eccdee84e8b9d8fde5f45356f68-1"&gt;&lt;/a&gt;&lt;span class="k"&gt;[data]&lt;/span&gt;
&lt;a name="rest_code_1cd80eccdee84e8b9d8fde5f45356f68-2"&gt;&lt;/a&gt;&lt;span class="na"&gt;...&lt;/span&gt;
&lt;a name="rest_code_1cd80eccdee84e8b9d8fde5f45356f68-3"&gt;&lt;/a&gt;&lt;span class="na"&gt;dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/mnt/storage/influxdb/data"&lt;/span&gt;
&lt;a name="rest_code_1cd80eccdee84e8b9d8fde5f45356f68-4"&gt;&lt;/a&gt;&lt;span class="na"&gt;wal-dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/var/lib/influxdb/wal"&lt;/span&gt;
&lt;a name="rest_code_1cd80eccdee84e8b9d8fde5f45356f68-5"&gt;&lt;/a&gt;&lt;span class="na"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="section" id="start-and-stop-scripts"&gt;
&lt;h4&gt;Start and Stop Scripts&lt;/h4&gt;
&lt;p&gt;Create two shell scripts to start and stop InfluxDB.&lt;/p&gt;
&lt;p&gt;The start script is included in &lt;code class="docutils literal"&gt;/opt/bootlocal.sh&lt;/code&gt;,
the stop script in &lt;code class="docutils literal"&gt;/opt/shutdown.sh&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The start script does the following:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;set permissions on pid file&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;set permissions on log file&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;start &lt;code class="docutils literal"&gt;influxd&lt;/code&gt; with parameters for pid file and configuration file
and redirect stderr to the logfile&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The stop script simply reads the process id from the pid file
and kills that process.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Start&lt;/strong&gt;&lt;/p&gt;
&lt;pre class="code bash"&gt;&lt;a name="rest_code_a2144dafda5b4a5bb4fff8efab86ebbf-1"&gt;&lt;/a&gt;&lt;span class="nv"&gt;CONFIG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/local/etc/influxdb/influxdb.conf
&lt;a name="rest_code_a2144dafda5b4a5bb4fff8efab86ebbf-2"&gt;&lt;/a&gt;&lt;span class="nv"&gt;PIDFILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/var/run/influxd.pid
&lt;a name="rest_code_a2144dafda5b4a5bb4fff8efab86ebbf-3"&gt;&lt;/a&gt;&lt;span class="nv"&gt;LOGFILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/var/log/influxdb.log
&lt;a name="rest_code_a2144dafda5b4a5bb4fff8efab86ebbf-4"&gt;&lt;/a&gt;
&lt;a name="rest_code_a2144dafda5b4a5bb4fff8efab86ebbf-5"&gt;&lt;/a&gt;touch &lt;span class="nv"&gt;$PIDFILE&lt;/span&gt;
&lt;a name="rest_code_a2144dafda5b4a5bb4fff8efab86ebbf-6"&gt;&lt;/a&gt;chown influxdb:nogroup &lt;span class="nv"&gt;$PIDFILE&lt;/span&gt;
&lt;a name="rest_code_a2144dafda5b4a5bb4fff8efab86ebbf-7"&gt;&lt;/a&gt;
&lt;a name="rest_code_a2144dafda5b4a5bb4fff8efab86ebbf-8"&gt;&lt;/a&gt;touch &lt;span class="nv"&gt;$LOGFILE&lt;/span&gt;
&lt;a name="rest_code_a2144dafda5b4a5bb4fff8efab86ebbf-9"&gt;&lt;/a&gt;chown influxdb:staff &lt;span class="nv"&gt;$LOGFILE&lt;/span&gt;
&lt;a name="rest_code_a2144dafda5b4a5bb4fff8efab86ebbf-10"&gt;&lt;/a&gt;
&lt;a name="rest_code_a2144dafda5b4a5bb4fff8efab86ebbf-11"&gt;&lt;/a&gt;su influxdb -s /bin/sh -c &lt;span class="s2"&gt;"influxd run -pidfile &lt;/span&gt;&lt;span class="nv"&gt;$PIDFILE&lt;/span&gt;&lt;span class="s2"&gt; -config &lt;/span&gt;&lt;span class="nv"&gt;$CONFIG&lt;/span&gt;&lt;span class="s2"&gt; 2&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;$LOGFILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Stop&lt;/strong&gt;&lt;/p&gt;
&lt;pre class="code bash"&gt;&lt;a name="rest_code_9c6028557fd54ad6a1bf314d14af8168-1"&gt;&lt;/a&gt;&lt;span class="nv"&gt;pid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;cat /var/run/influxd.pid&lt;span class="k"&gt;)&lt;/span&gt;
&lt;a name="rest_code_9c6028557fd54ad6a1bf314d14af8168-2"&gt;&lt;/a&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; -n &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$pid&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;a name="rest_code_9c6028557fd54ad6a1bf314d14af8168-3"&gt;&lt;/a&gt;    &lt;span class="nb"&gt;kill&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$pid&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;a name="rest_code_9c6028557fd54ad6a1bf314d14af8168-4"&gt;&lt;/a&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="backup"&gt;
&lt;h3&gt;Backup&lt;/h3&gt;
&lt;p&gt;There a several options for backing up InfluxDB.
On can backup the full database(s) or only selected retention policies.
It is also possible to only back up data since a specific timestamp.&lt;/p&gt;
&lt;p&gt;The backup can be run locally or over the network
and it can be performed while InfluxDB is running.&lt;/p&gt;
&lt;p&gt;For the RaspberryPi, we create full backups, using a local cron job.
Backups are created daily and a number of the most recent backups is retained.&lt;/p&gt;
&lt;p&gt;The basic steps are as follows:
- backup the &lt;em&gt;Metastore&lt;/em&gt; and selected &lt;em&gt;Databases&lt;/em&gt; to a temporary location
- if all went well, compress the result (.tar.gz)
- delete the oldest backup&lt;/p&gt;
&lt;p&gt;To backup the &lt;strong&gt;Metastore&lt;/strong&gt;:&lt;/p&gt;
&lt;pre class="code bash"&gt;&lt;a name="rest_code_8118269999344ef99727af8ae4a912c1-1"&gt;&lt;/a&gt;influxd backup /path/to/backup
&lt;/pre&gt;&lt;p&gt;Each &lt;strong&gt;Database&lt;/strong&gt; is backed up individually by calling&lt;/p&gt;
&lt;pre class="code bash"&gt;&lt;a name="rest_code_5b0d3e8a266d4764b84fe28fc50dabf9-1"&gt;&lt;/a&gt;influxd backup -database &amp;lt;NAME&amp;gt; /path/to/backup
&lt;/pre&gt;&lt;p&gt;See &lt;a class="reference external" href="https://docs.influxdata.com/influxdb/v1.3/administration/backup_and_restore/"&gt;InfluxDB backup and restore&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/influxdb-for-tinycore-linux/#id11" id="id12"&gt;6&lt;/a&gt; for details.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="monitoring-with-monit"&gt;
&lt;h3&gt;Monitoring with Monit&lt;/h3&gt;
&lt;p&gt;Use the following checks with &lt;a class="reference external" href="https://mmonit.com/monit/documentation/monit.html"&gt;monit&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/influxdb-for-tinycore-linux/#id13" id="id14"&gt;7&lt;/a&gt;:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Check whether the process exists using the &lt;code class="docutils literal"&gt;pidfile&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Make sure it is run by the &lt;code class="docutils literal"&gt;influxdb&lt;/code&gt; user&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Call the HTTP interface on the &lt;code class="docutils literal"&gt;/ping&lt;/code&gt; endpoint&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Check if there is a recent backup&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are the &lt;code class="docutils literal"&gt;monit&lt;/code&gt; checks:&lt;/p&gt;
&lt;pre class="literal-block"&gt;check process influxd pidfile /var/run/influxd.pid
    group influxdb
    if failed uid influxdb then alert
    if failed
        port 8086
        protocol http
        request /ping
        status = 204
    then alert

check file influxdbbackup path /mnt/storage/influxdb/backups/snapshot.0.tar.gz
    group backup
    if does not exist then alert
    if timestamp &amp;gt; 25 hours then alert&lt;/pre&gt;
&lt;hr class="docutils"&gt;
&lt;dl class="footnote brackets"&gt;
&lt;dt class="label" id="id1"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/influxdb-for-tinycore-linux/#id2"&gt;1&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="https://www.influxdata.com/time-series-platform/influxdb/"&gt;https://www.influxdata.com/time-series-platform/influxdb/&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id3"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/influxdb-for-tinycore-linux/#id4"&gt;2&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="http://tinycorelinux.net/"&gt;http://tinycorelinux.net/&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id5"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/influxdb-for-tinycore-linux/#id6"&gt;3&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="http://wiki.tinycorelinux.net/wiki:creating_extensions"&gt;http://wiki.tinycorelinux.net/wiki:creating_extensions&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id7"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/influxdb-for-tinycore-linux/#id8"&gt;4&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="https://portal.influxdata.com/downloads#influxdb"&gt;https://portal.influxdata.com/downloads#influxdb&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id9"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/influxdb-for-tinycore-linux/#id10"&gt;5&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="https://docs.influxdata.com/influxdb/v1.3/concepts/storage_engine/"&gt;https://docs.influxdata.com/influxdb/v1.3/concepts/storage_engine/&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id11"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/influxdb-for-tinycore-linux/#id12"&gt;6&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="https://docs.influxdata.com/influxdb/v1.3/administration/backup_and_restore/"&gt;https://docs.influxdata.com/influxdb/v1.3/administration/backup_and_restore/&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id13"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/influxdb-for-tinycore-linux/#id14"&gt;7&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="https://mmonit.com/monit/documentation/monit.html"&gt;https://mmonit.com/monit/documentation/monit.html&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/div&gt;
&lt;/div&gt;</description><category>homeserver</category><category>influxdb</category><category>linux</category><category>raspi</category><category>tinycore</category><guid>https://akeil.de/posts/influxdb-for-tinycore-linux/</guid><pubDate>Sat, 12 Aug 2017 22:00:00 GMT</pubDate></item><item><title>MQTT Bridge with Mosquitto and nginx</title><link>https://akeil.de/posts/mqtt-bridge-with-mosquitto-and-nginx/</link><dc:creator>Alexander Keil</dc:creator><description>&lt;div class="section" id="mqtt-bridge-with-mosquitto-and-nginx"&gt;
&lt;h2&gt;MQTT Bridge with Mosquitto and nginx&lt;/h2&gt;
&lt;dl class="field-list simple"&gt;
&lt;dt&gt;author&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;akeil&lt;/p&gt;
&lt;/dd&gt;
&lt;dt&gt;date&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;2017-03-05&lt;/p&gt;
&lt;/dd&gt;
&lt;dt&gt;version&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;1&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;Set up a &lt;a class="reference external" href="https://mosquitto.org/"&gt;Mosquitto&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/mqtt-bridge-with-mosquitto-and-nginx/#id2" id="id3"&gt;1&lt;/a&gt; MQTT broker which is available in the internet.
&lt;a class="reference external" href="https://nginx.com/"&gt;nginx&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/mqtt-bridge-with-mosquitto-and-nginx/#id4" id="id5"&gt;2&lt;/a&gt; is used as a reverse proxy and to handle SSL encryption.
An additional Mosquitto instance on the local network is used as a &lt;em&gt;bridge&lt;/em&gt;
to forward MQTT messages from the local network to the internet and vice versa.&lt;/p&gt;
&lt;p&gt;This allows to easily connect devices which do not support authentication
or encryption on the local network with with other devices or services
connected via the internet.&lt;/p&gt;
&lt;div class="figure"&gt;
&lt;img alt="/images/mqtt-bridge-overview.png" src="https://akeil.de/images/mqtt-bridge-overview.png"&gt;
&lt;p class="caption"&gt;Overview for an MQTT bridge scenario.
Clients on the local network use an insecure connection to talk
to the local MQTT broker
while clients on the internet use a secure connection.&lt;/p&gt;
&lt;/div&gt;
&lt;!-- diagram made with draw.io --&gt;
&lt;!-- TEASER_END --&gt;
&lt;div class="section" id="mosquitto-public"&gt;
&lt;h3&gt;Mosquitto (public)&lt;/h3&gt;
&lt;p&gt;The public Mosquitto instance needs some configuration changes.
The configuration file is located at &lt;code class="docutils literal"&gt;/etc/mosquitto/mosquitto.conf&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Since we will be running behind nginx, make the MQTT service listen on
localhost only:&lt;/p&gt;
&lt;pre class="code text"&gt;&lt;a name="rest_code_bea6e6ae9f914ac8b42e7727db043b13-1"&gt;&lt;/a&gt;bind_address localhost
&lt;/pre&gt;&lt;div class="topic"&gt;
&lt;p class="topic-title"&gt;Change MQTT Service Port (optional)&lt;/p&gt;
&lt;p&gt;Change the default port number from &lt;code class="docutils literal"&gt;1883&lt;/code&gt; to something else:&lt;/p&gt;
&lt;pre class="code text"&gt;&lt;a name="rest_code_9d69e1563abc494d89dde972a0b4de1b-1"&gt;&lt;/a&gt;listener 9883
&lt;/pre&gt;&lt;p&gt;This is strictly not necessary as the default is normally used for
unencrypted connections and we will configure nginx to listen
on port &lt;code class="docutils literal"&gt;8883&lt;/code&gt; (the default port for MQTT over SSL).&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Finally, set up mosquitto to require authentication with username and password:&lt;/p&gt;
&lt;pre class="literal-block"&gt;allow_anonymous false
password_file = /etc/mosquitto/passwords&lt;/pre&gt;
&lt;p&gt;Create the password file and the first user with:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_7a1323342992457c820ed9bf094b0f7f-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;#&lt;/span&gt; mosquitto_passwd -c /etc/mosquitto/passwords username
&lt;/pre&gt;&lt;p&gt;&lt;em&gt;(You will be prompted for a password.)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;To test authentication, attempt to subscribe to any topic.
A valid username and password should work, a subscription attempt
with invalid credentials should be rejected.&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_14ecd786c93b426c8bb096f8826de167-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; mosquitto_sub -p &lt;span class="m"&gt;9883&lt;/span&gt; -t &lt;span class="s1"&gt;'#'&lt;/span&gt; -u username -P secret
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="section" id="id1"&gt;
&lt;h3&gt;nginx&lt;/h3&gt;
&lt;p&gt;Configure &lt;a class="reference external" href="https://www.nginx.com/resources/admin-guide/tcp-load-balancing/"&gt;nginx as a reverse proxy for TCP streams&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/mqtt-bridge-with-mosquitto-and-nginx/#id6" id="id7"&gt;3&lt;/a&gt;
and to &lt;a class="reference external" href="https://www.nginx.com/resources/admin-guide/nginx-tcp-ssl-termination/"&gt;terminate the SSL encryption&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/mqtt-bridge-with-mosquitto-and-nginx/#id8" id="id9"&gt;4&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;First, define Mosquitto as an &lt;em&gt;upstream&lt;/em&gt; service.
Use either &lt;code class="docutils literal"&gt;1883&lt;/code&gt; or whatever alternative port is set for the
default (unencrypted) MQTT service.&lt;/p&gt;
&lt;p&gt;Next, set up a &lt;em&gt;server&lt;/em&gt; to listen on port &lt;code class="docutils literal"&gt;8883&lt;/code&gt; (MQTT over SSL)
and tell it to pass requests to the upstream service.&lt;/p&gt;
&lt;p&gt;To enable SSL, configure the location of the SSL certificate
and private key.
Set additional SSL options as needed; in this case taken from &lt;a class="reference external" href="https://cipherli.st/"&gt;cipherli.st&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/mqtt-bridge-with-mosquitto-and-nginx/#id10" id="id11"&gt;5&lt;/a&gt;.&lt;/p&gt;
&lt;div class="admonition note"&gt;
&lt;p class="admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;The example assumes a &lt;a class="reference external" href="https://letsencrypt.org/"&gt;Let's Encrypt&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/mqtt-bridge-with-mosquitto-and-nginx/#id12" id="id13"&gt;6&lt;/a&gt; certificate.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The nginx configuration looks like this
(with &lt;code class="docutils literal"&gt;stream&lt;/code&gt; being a toplevel-directive):&lt;/p&gt;
&lt;pre class="code nginx"&gt;&lt;a name="rest_code_dd1d0578bf2e43beb58766208846e165-1"&gt;&lt;/a&gt;&lt;span class="k"&gt;stream&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;a name="rest_code_dd1d0578bf2e43beb58766208846e165-2"&gt;&lt;/a&gt;
&lt;a name="rest_code_dd1d0578bf2e43beb58766208846e165-3"&gt;&lt;/a&gt;    &lt;span class="kn"&gt;upstream&lt;/span&gt; &lt;span class="s"&gt;mosquitto&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;a name="rest_code_dd1d0578bf2e43beb58766208846e165-4"&gt;&lt;/a&gt;        &lt;span class="kn"&gt;server&lt;/span&gt; &lt;span class="n"&gt;localhost&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;9883&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a name="rest_code_dd1d0578bf2e43beb58766208846e165-5"&gt;&lt;/a&gt;    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;a name="rest_code_dd1d0578bf2e43beb58766208846e165-6"&gt;&lt;/a&gt;
&lt;a name="rest_code_dd1d0578bf2e43beb58766208846e165-7"&gt;&lt;/a&gt;    &lt;span class="kn"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;a name="rest_code_dd1d0578bf2e43beb58766208846e165-8"&gt;&lt;/a&gt;        &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;8883&lt;/span&gt; &lt;span class="s"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a name="rest_code_dd1d0578bf2e43beb58766208846e165-9"&gt;&lt;/a&gt;        &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;mosquitto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a name="rest_code_dd1d0578bf2e43beb58766208846e165-10"&gt;&lt;/a&gt;
&lt;a name="rest_code_dd1d0578bf2e43beb58766208846e165-11"&gt;&lt;/a&gt;        &lt;span class="kn"&gt;ssl_certificate&lt;/span&gt; &lt;span class="s"&gt;/etc/letsencrypt/live/example.com/fullchain.pem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a name="rest_code_dd1d0578bf2e43beb58766208846e165-12"&gt;&lt;/a&gt;        &lt;span class="kn"&gt;ssl_certificate_key&lt;/span&gt; &lt;span class="s"&gt;/etc/letsencrypt/live/example.com/privkey.pem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a name="rest_code_dd1d0578bf2e43beb58766208846e165-13"&gt;&lt;/a&gt;
&lt;a name="rest_code_dd1d0578bf2e43beb58766208846e165-14"&gt;&lt;/a&gt;        &lt;span class="c1"&gt;# from https://cipherli.st/&lt;/span&gt;
&lt;a name="rest_code_dd1d0578bf2e43beb58766208846e165-15"&gt;&lt;/a&gt;        &lt;span class="kn"&gt;ssl_protocols&lt;/span&gt; &lt;span class="s"&gt;TLSv1&lt;/span&gt; &lt;span class="s"&gt;TLSv1.1&lt;/span&gt; &lt;span class="s"&gt;TLSv1.2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a name="rest_code_dd1d0578bf2e43beb58766208846e165-16"&gt;&lt;/a&gt;        &lt;span class="kn"&gt;ssl_prefer_server_ciphers&lt;/span&gt; &lt;span class="no"&gt;on&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a name="rest_code_dd1d0578bf2e43beb58766208846e165-17"&gt;&lt;/a&gt;        &lt;span class="kn"&gt;ssl_ciphers&lt;/span&gt; &lt;span class="s"&gt;"EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a name="rest_code_dd1d0578bf2e43beb58766208846e165-18"&gt;&lt;/a&gt;        &lt;span class="kn"&gt;ssl_ecdh_curve&lt;/span&gt; &lt;span class="s"&gt;secp384r1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a name="rest_code_dd1d0578bf2e43beb58766208846e165-19"&gt;&lt;/a&gt;        &lt;span class="kn"&gt;ssl_session_cache&lt;/span&gt; &lt;span class="s"&gt;shared:SSL:10m&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a name="rest_code_dd1d0578bf2e43beb58766208846e165-20"&gt;&lt;/a&gt;        &lt;span class="kn"&gt;ssl_session_tickets&lt;/span&gt; &lt;span class="no"&gt;off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a name="rest_code_dd1d0578bf2e43beb58766208846e165-21"&gt;&lt;/a&gt;
&lt;a name="rest_code_dd1d0578bf2e43beb58766208846e165-22"&gt;&lt;/a&gt;        &lt;span class="kn"&gt;ssl_dhparam&lt;/span&gt; &lt;span class="s"&gt;/etc/ssl/certs/dhparam.pem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a name="rest_code_dd1d0578bf2e43beb58766208846e165-23"&gt;&lt;/a&gt;    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;a name="rest_code_dd1d0578bf2e43beb58766208846e165-24"&gt;&lt;/a&gt;
&lt;a name="rest_code_dd1d0578bf2e43beb58766208846e165-25"&gt;&lt;/a&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="section" id="mqtt-bridge"&gt;
&lt;h3&gt;MQTT-Bridge&lt;/h3&gt;
&lt;p&gt;On the local (LAN) MQTT broker, edit the configuration to add a &lt;em&gt;bridge&lt;/em&gt;:&lt;/p&gt;
&lt;pre class="code text"&gt;&lt;a name="rest_code_9ff809e2838641f5a9206e49a89787e6-1"&gt;&lt;/a&gt;connection bridge
&lt;a name="rest_code_9ff809e2838641f5a9206e49a89787e6-2"&gt;&lt;/a&gt;address &amp;lt;ip-or-hostname&amp;gt;:8883
&lt;a name="rest_code_9ff809e2838641f5a9206e49a89787e6-3"&gt;&lt;/a&gt;
&lt;a name="rest_code_9ff809e2838641f5a9206e49a89787e6-4"&gt;&lt;/a&gt;remote_username foo
&lt;a name="rest_code_9ff809e2838641f5a9206e49a89787e6-5"&gt;&lt;/a&gt;remote_password xxx
&lt;a name="rest_code_9ff809e2838641f5a9206e49a89787e6-6"&gt;&lt;/a&gt;
&lt;a name="rest_code_9ff809e2838641f5a9206e49a89787e6-7"&gt;&lt;/a&gt;remote_clientid LAN-broker
&lt;a name="rest_code_9ff809e2838641f5a9206e49a89787e6-8"&gt;&lt;/a&gt;local_client public-broker
&lt;a name="rest_code_9ff809e2838641f5a9206e49a89787e6-9"&gt;&lt;/a&gt;
&lt;a name="rest_code_9ff809e2838641f5a9206e49a89787e6-10"&gt;&lt;/a&gt;# must specify cafile to enable SSL
&lt;a name="rest_code_9ff809e2838641f5a9206e49a89787e6-11"&gt;&lt;/a&gt;bridge_cafile /etc/ssl/certs/DST_Root_CA_X3.pem
&lt;a name="rest_code_9ff809e2838641f5a9206e49a89787e6-12"&gt;&lt;/a&gt;bridge_insecure false
&lt;a name="rest_code_9ff809e2838641f5a9206e49a89787e6-13"&gt;&lt;/a&gt;
&lt;a name="rest_code_9ff809e2838641f5a9206e49a89787e6-14"&gt;&lt;/a&gt;# topic mappings
&lt;a name="rest_code_9ff809e2838641f5a9206e49a89787e6-15"&gt;&lt;/a&gt;# topic PATTERN [out|in|both] QOS LOCAL-PREFIX REMOTE-PREFIX
&lt;a name="rest_code_9ff809e2838641f5a9206e49a89787e6-16"&gt;&lt;/a&gt;topic foo both 0
&lt;/pre&gt;&lt;p&gt;Remember to specify a port with the bridge &lt;code class="docutils literal"&gt;connection&lt;/code&gt;, otherwise &lt;code class="docutils literal"&gt;1883&lt;/code&gt; is used.
The &lt;code class="docutils literal"&gt;bridge_cafile&lt;/code&gt; setting is required to enable SSL.
See &lt;a class="reference external" href="https://mosquitto.org/man/mosquitto-conf-5.html"&gt;Mosquitto conf&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/mqtt-bridge-with-mosquitto-and-nginx/#id14" id="id15"&gt;7&lt;/a&gt; for more details.&lt;/p&gt;
&lt;p&gt;Restart mosquitto to apply the new settings.&lt;/p&gt;
&lt;p&gt;The Mosquitto log file for the local broker should contain a line like this:&lt;/p&gt;
&lt;pre class="literal-block"&gt;Connecting bridge bridge (&amp;lt;ip-or-hostname&amp;gt;:8883)&lt;/pre&gt;
&lt;p&gt;On the remote side, the bridge looks like a normal client connection:&lt;/p&gt;
&lt;pre class="literal-block"&gt;New client connected from ::1 as &amp;lt;remote_clientid&amp;gt; (c0, k60, u'&amp;lt;remote_username&amp;gt;').&lt;/pre&gt;
&lt;p&gt;To test, subscribe to &lt;code class="docutils literal"&gt;foo&lt;/code&gt; on the local broker...&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_f2ba256a31244013928c266544b31e79-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; mosquitto_sub -h &amp;lt;localbroker&amp;gt; -t foo
&lt;/pre&gt;&lt;p&gt;... and publish to &lt;code class="docutils literal"&gt;foo&lt;/code&gt; on the remote broker:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_7d892eae489548449c9bf29924721b9d-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; mosquitto_pub -h &amp;lt;remotebroker&amp;gt; -p &lt;span class="m"&gt;8883&lt;/span&gt; -u username -P secret -t foo -m &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;You should see the "test" message appear on the session subscribed
to the local broker.&lt;/p&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title"&gt;Topic Mappings&lt;/p&gt;
&lt;p&gt;A &lt;em&gt;topic mapping&lt;/em&gt; consists of a &lt;em&gt;pattern&lt;/em&gt; to subscribe to,
the direction of the messages (&lt;em&gt;in&lt;/em&gt;, &lt;em&gt;out&lt;/em&gt; or &lt;em&gt;both&lt;/em&gt;
and a &lt;em&gt;QoS&lt;/em&gt; setting.
Additionally, a &lt;em&gt;local prefix&lt;/em&gt; and &lt;em&gt;remote prefix&lt;/em&gt; can be defined.&lt;/p&gt;
&lt;p&gt;Only the &lt;em&gt;pattern&lt;/em&gt; is required.
Direction defaults to &lt;code class="docutils literal"&gt;out&lt;/code&gt;, &lt;em&gt;QoS&lt;/em&gt; defaults to &lt;code class="docutils literal"&gt;0&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Examples:&lt;/p&gt;
&lt;pre class="literal-block"&gt;# forward all 'foo' messages to the bridge
topic foo
topic foo out 0  # same as above

# two-way forwarding for everything under "foo"
topic foo/# both

# (only) receive all foo messages
topic foo/# in&lt;/pre&gt;
&lt;p&gt;This can be used to forward only selected topics
and to insert topics from the bridged broker into the
local topic tree.&lt;/p&gt;
&lt;p&gt;If no &lt;code class="docutils literal"&gt;topic&lt;/code&gt; directive is present, no messages are
forwarded across the bridge.&lt;/p&gt;
&lt;/div&gt;
&lt;hr class="docutils"&gt;
&lt;dl class="footnote brackets"&gt;
&lt;dt class="label" id="id2"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/mqtt-bridge-with-mosquitto-and-nginx/#id3"&gt;1&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="https://mosquitto.org/"&gt;https://mosquitto.org/&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id4"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/mqtt-bridge-with-mosquitto-and-nginx/#id5"&gt;2&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="https://nginx.com/"&gt;https://nginx.com/&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id6"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/mqtt-bridge-with-mosquitto-and-nginx/#id7"&gt;3&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="https://www.nginx.com/resources/admin-guide/tcp-load-balancing/"&gt;https://www.nginx.com/resources/admin-guide/tcp-load-balancing/&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id8"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/mqtt-bridge-with-mosquitto-and-nginx/#id9"&gt;4&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="https://www.nginx.com/resources/admin-guide/nginx-tcp-ssl-termination/"&gt;https://www.nginx.com/resources/admin-guide/nginx-tcp-ssl-termination/&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id10"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/mqtt-bridge-with-mosquitto-and-nginx/#id11"&gt;5&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="https://cipherli.st/"&gt;https://cipherli.st/&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id12"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/mqtt-bridge-with-mosquitto-and-nginx/#id13"&gt;6&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="https://letsencrypt.org/"&gt;https://letsencrypt.org/&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id14"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/mqtt-bridge-with-mosquitto-and-nginx/#id15"&gt;7&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="https://mosquitto.org/man/mosquitto-conf-5.html"&gt;https://mosquitto.org/man/mosquitto-conf-5.html&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/div&gt;
&lt;/div&gt;</description><category>linux</category><category>mqtt</category><category>nginx</category><guid>https://akeil.de/posts/mqtt-bridge-with-mosquitto-and-nginx/</guid><pubDate>Sun, 05 Mar 2017 10:50:00 GMT</pubDate></item><item><title>Mosquitto MQTT on TinyCore Linux</title><link>https://akeil.de/posts/mosquitto-mqtt-on-tinycore/</link><dc:creator>Alexander Keil</dc:creator><description>&lt;div class="section" id="mosquitto-mqtt-on-tinycore-linux"&gt;
&lt;h2&gt;Mosquitto MQTT on TinyCore Linux&lt;/h2&gt;
&lt;dl class="field-list simple"&gt;
&lt;dt&gt;author&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;akeil&lt;/p&gt;
&lt;/dd&gt;
&lt;dt&gt;date&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;2017-01-14&lt;/p&gt;
&lt;/dd&gt;
&lt;dt&gt;version&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;1&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;This post describes how to install the &lt;a class="reference external" href="http://mosquitto.org/"&gt;Mosquitto&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/mosquitto-mqtt-on-tinycore/#id1" id="id2"&gt;1&lt;/a&gt; &lt;a class="reference external" href="http://mqtt.org/"&gt;MQTT&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/mosquitto-mqtt-on-tinycore/#id3" id="id4"&gt;2&lt;/a&gt; broker on a Raspberry Pi
with &lt;a class="reference external" href="http://tinycorelinux.net/"&gt;TinyCore&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/mosquitto-mqtt-on-tinycore/#id5" id="id6"&gt;3&lt;/a&gt; Linux.&lt;/p&gt;
&lt;!-- TEASER_END --&gt;
&lt;div class="section" id="build-an-extension"&gt;
&lt;h3&gt;Build an Extension&lt;/h3&gt;
&lt;p&gt;There is no prebuilt extension for Mosquitto which means that have to build the
program from source and create a custom extension for TinyCore Linux.&lt;/p&gt;
&lt;p&gt;First, download the source and unpack:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_f31ee193126f4b219dc12a828af02af8-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; wget http://mosquitto.org/files/source/mosquitto-1.4.10.tar.gz
&lt;a name="rest_code_f31ee193126f4b219dc12a828af02af8-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; tar -xzf mosquitto-1.4.10.tar.gz
&lt;/pre&gt;&lt;p&gt;Next, install build dependencies.&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_10a5734abdd34662b33b6a3708197c98-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; tce-load -w make
&lt;a name="rest_code_10a5734abdd34662b33b6a3708197c98-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; tce-load -w gcc
&lt;a name="rest_code_10a5734abdd34662b33b6a3708197c98-3"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; tce-load -w compiletc
&lt;a name="rest_code_10a5734abdd34662b33b6a3708197c98-4"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; tce-load -w openssl-dev
&lt;a name="rest_code_10a5734abdd34662b33b6a3708197c98-5"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; tce-load -w squashfs-tools
&lt;a name="rest_code_10a5734abdd34662b33b6a3708197c98-6"&gt;&lt;/a&gt;
&lt;a name="rest_code_10a5734abdd34662b33b6a3708197c98-7"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; tce-load -i make
&lt;a name="rest_code_10a5734abdd34662b33b6a3708197c98-8"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; tce-load -i gcc
&lt;a name="rest_code_10a5734abdd34662b33b6a3708197c98-9"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; tce-load -i compiletc
&lt;a name="rest_code_10a5734abdd34662b33b6a3708197c98-10"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; tce-load -i openssl-dev
&lt;a name="rest_code_10a5734abdd34662b33b6a3708197c98-11"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; tce-load -i squashfs-tools
&lt;/pre&gt;&lt;div class="section" id="edit-makefiles"&gt;
&lt;h4&gt;Edit Makefiles&lt;/h4&gt;
&lt;p&gt;There are several Makefiles in the Mosquitto project directory.
Some of these contain the command &lt;code class="docutils literal"&gt;install &lt;span class="pre"&gt;-s&lt;/span&gt; &lt;span class="pre"&gt;--strip-program&lt;/span&gt; foo&lt;/code&gt; but the
&lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;--strip-program&lt;/span&gt;&lt;/code&gt; option is not supported by busybox &lt;em&gt;install&lt;/em&gt;.
Remove that option (not the call)  in all affected Makefiles:&lt;/p&gt;
&lt;pre class="literal-block"&gt;./Makefile
./lib/Makefile
./lib/cpp/Makefile
./client/Makefile
./src/Makefile&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="build-and-install"&gt;
&lt;h4&gt;Build and Install&lt;/h4&gt;
&lt;p&gt;According to the Mosquitto README, optional dependencies are &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;c-ares&lt;/span&gt;&lt;/code&gt;
and &lt;code class="docutils literal"&gt;libuuid&lt;/code&gt;, both of which are not available.
So we will call &lt;code class="docutils literal"&gt;make WITH_SRV=no WITH_UUID=no&lt;/code&gt; to build.&lt;/p&gt;
&lt;div class="admonition note"&gt;
&lt;p class="admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;The README says to use &lt;code class="docutils literal"&gt;WITH_DNS_SRV=no&lt;/code&gt; to build without &lt;em&gt;ares&lt;/em&gt;.
We need to do this if we do not want to build ares ourselves.
This is not quite correct, it is &lt;code class="docutils literal"&gt;WITH_SRV=no&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_22316e81d2db44459627938dd61b944d-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; mosquitto-1.4.10/
&lt;a name="rest_code_22316e81d2db44459627938dd61b944d-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; make
&lt;a name="rest_code_22316e81d2db44459627938dd61b944d-3"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; make &lt;span class="nv"&gt;WITH_SRV&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;no &lt;span class="nv"&gt;WITH_UUID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;no
&lt;/pre&gt;&lt;p&gt;Install into &lt;code class="docutils literal"&gt;/tmp&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_ec671fabed224444bebfe6486436565e-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo make install &lt;span class="nv"&gt;WITH_DOCS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;no &lt;span class="nv"&gt;DESTDIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/tmp/mosquitto
&lt;/pre&gt;&lt;div class="sidebar"&gt;
&lt;p class="sidebar-title"&gt;TinyCore Extensions&lt;/p&gt;
&lt;p&gt;Information on how to create tce's can be found in the
&lt;a class="reference external" href="http://distro.ibiblio.org/tinycorelinux/corebook.pdf"&gt;TinyCore Book&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/mosquitto-mqtt-on-tinycore/#id7" id="id8"&gt;4&lt;/a&gt; [PDF] and in the &lt;a class="reference external" href="http://wiki.tinycorelinux.net/wiki:creating_extensions"&gt;TinyCore Wiki&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/mosquitto-mqtt-on-tinycore/#id9" id="id10"&gt;5&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Installation is done as root so we have the correct permissions
on all installed files.&lt;/p&gt;
&lt;p&gt;TinyCore Linux recommends to &lt;em&gt;not&lt;/em&gt; include the docs in the extension
(optionally package them in a separate -docs extension). The &lt;code class="docutils literal"&gt;WITH_DOCS=no&lt;/code&gt;
option does exactly this.&lt;/p&gt;
&lt;p&gt;The TinyCore Linux guide recommends to &lt;em&gt;strip&lt;/em&gt; the created binaries.
This has already been done in the Makefiles with &lt;code class="docutils literal"&gt;install &lt;span class="pre"&gt;-s&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;After installation, all relevant files are located in &lt;code class="docutils literal"&gt;/tmp/mosquitto&lt;/code&gt;.
Execute &lt;code class="docutils literal"&gt;/tmp/mosquitto/usr/local/sbin/mosquitto&lt;/code&gt; to make a test-run.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="create-the-extension"&gt;
&lt;h4&gt;Create the Extension&lt;/h4&gt;
&lt;p&gt;To pack the extension:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_c3f7778fa1b9442dbeffe298d8ad2d68-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; mksquashfs /tmp/mosquitto/ /tmp/mosquitto.tcz
&lt;a name="rest_code_c3f7778fa1b9442dbeffe298d8ad2d68-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; rm -rf /tmp/mosquitto
&lt;a name="rest_code_c3f7778fa1b9442dbeffe298d8ad2d68-3"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; mv /tmp/mosquitto.tcz /etc/sysconfig/tcedir/optional/
&lt;/pre&gt;&lt;p&gt;Then to install&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_26cbeb8c112e4d5c97ea4ca39210c1c8-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; tce-load -i mosquitto
&lt;/pre&gt;&lt;p&gt;To load on boot:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_35708bb8a89c471096a62ad7e499a8f9-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; mosquitto.tcz &amp;gt;&amp;gt; /etc/sysconfig/tcedir/onboot.lst
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="create-user"&gt;
&lt;h3&gt;Create User&lt;/h3&gt;
&lt;p&gt;To run Mosquitto with a dedicated user, create that user:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_188ad91c434b403fae577945d2fb6e15-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo adduser -S mosquitto
&lt;/pre&gt;&lt;p&gt;And edit configuration accordingly.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="configuration"&gt;
&lt;h3&gt;Configuration&lt;/h3&gt;
&lt;p&gt;The config file goes into &lt;code class="docutils literal"&gt;/usr/local/etc/mosquitto.conf&lt;/code&gt;.
This is not the default location, so we need to use the &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;--config-file&lt;/span&gt;&lt;/code&gt;
option when starting Mosquitto to tell it where to look for configuration.&lt;/p&gt;
&lt;p&gt;Of course, the config file should be included in the list of files
to be backed up and restored on boot.
Include it in &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;/opt/.filetool.lst&lt;/span&gt;&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="literal-block"&gt;usr/local/etc/mosquitto.conf&lt;/pre&gt;
&lt;div class="section" id="logging"&gt;
&lt;h4&gt;Logging&lt;/h4&gt;
&lt;p&gt;TinyCore Linux does not come with a syslog daemon. If logging is desired,
write to a log file:&lt;/p&gt;
&lt;pre class="literal-block"&gt;log_dest file /var/log/mosquitto.log&lt;/pre&gt;
&lt;p&gt;On boot, make sure that the file is writable for the &lt;code class="docutils literal"&gt;mosquitto&lt;/code&gt; user.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="persistence"&gt;
&lt;h4&gt;Persistence&lt;/h4&gt;
&lt;p&gt;Mosquitto can write connection, subscription and message data to disk
and reload it on every restart.
To enable this in combination with TinyCore Linux, configure Mosquitto with persistence
and include the &lt;code class="docutils literal"&gt;mosquitto.db&lt;/code&gt; in backup/restore.&lt;/p&gt;
&lt;p&gt;Configuration:&lt;/p&gt;
&lt;pre class="literal-block"&gt;persistence true
persistence_location /var/lib/mosquitto/
persistence_file mosquitto.db
autosave_interval 1800&lt;/pre&gt;
&lt;p&gt;The &lt;code class="docutils literal"&gt;autosave_interval&lt;/code&gt; means that data is saved every 1800 seconds
(30 minutes). Additionally, state is stored on exit.&lt;/p&gt;
&lt;p&gt;The persistence directory should be created and chown`ed to &lt;code class="docutils literal"&gt;mosquitto&lt;/code&gt;
on boot.&lt;/p&gt;
&lt;p&gt;To include it in backup and restore, include it in &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;/opt/.filetool.lst&lt;/span&gt;&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="literal-block"&gt;var/lib/mosquitto&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="pid-file"&gt;
&lt;h4&gt;PID File&lt;/h4&gt;
&lt;p&gt;To help with shutdown and monitoring, we let Mosquitto write a pidfile:&lt;/p&gt;
&lt;pre class="literal-block"&gt;pid_file /var/run/mosquitto.pid&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="start-on-boot"&gt;
&lt;h3&gt;Start on boot&lt;/h3&gt;
&lt;p&gt;To start the service on boot, include this in &lt;code class="docutils literal"&gt;/opt/bootlocal.sh&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_d6c7c2f72fd14373b35f2895f909e1ac-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;#&lt;/span&gt; start Mosquitto in background
&lt;a name="rest_code_d6c7c2f72fd14373b35f2895f909e1ac-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;touch /var/log/mosquitto.log&lt;/span&gt;
&lt;a name="rest_code_d6c7c2f72fd14373b35f2895f909e1ac-3"&gt;&lt;/a&gt;&lt;span class="go"&gt;chown mosquitto:staff /var/log/mosquitto.log&lt;/span&gt;
&lt;a name="rest_code_d6c7c2f72fd14373b35f2895f909e1ac-4"&gt;&lt;/a&gt;&lt;span class="go"&gt;mkdir -p /var/lib/mosquitto&lt;/span&gt;
&lt;a name="rest_code_d6c7c2f72fd14373b35f2895f909e1ac-5"&gt;&lt;/a&gt;&lt;span class="go"&gt;chown mosquitto:staff /var/lib/mosquitto&lt;/span&gt;
&lt;a name="rest_code_d6c7c2f72fd14373b35f2895f909e1ac-6"&gt;&lt;/a&gt;&lt;span class="go"&gt;mosquitto --daemon --config-file /usr/local/etc/mosquitto.conf&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;This will start Mosquitto as root &lt;em&gt;unless&lt;/em&gt; the configuration file
defines the user to run as:&lt;/p&gt;
&lt;pre class="literal-block"&gt;user mosquitto&lt;/pre&gt;
&lt;p&gt;Which is recommended.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="shutdown"&gt;
&lt;h3&gt;Shutdown&lt;/h3&gt;
&lt;p&gt;To shutdown gracefully, include this in &lt;code class="docutils literal"&gt;/opt/shutdown.sh&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code bash"&gt;&lt;a name="rest_code_476e78d71582428d884a40a59d34a9ae-1"&gt;&lt;/a&gt;&lt;span class="c1"&gt;# stop mosquitto&lt;/span&gt;
&lt;a name="rest_code_476e78d71582428d884a40a59d34a9ae-2"&gt;&lt;/a&gt;&lt;span class="nv"&gt;mosquitto_pid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;cat /var/run/mosquitto.pid&lt;span class="k"&gt;)&lt;/span&gt;
&lt;a name="rest_code_476e78d71582428d884a40a59d34a9ae-3"&gt;&lt;/a&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; -n &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$mosquitto_pid&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;a name="rest_code_476e78d71582428d884a40a59d34a9ae-4"&gt;&lt;/a&gt;    &lt;span class="nb"&gt;kill&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$pid&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;a name="rest_code_476e78d71582428d884a40a59d34a9ae-5"&gt;&lt;/a&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="section" id="monitoring-with-monit"&gt;
&lt;h3&gt;Monitoring with monit&lt;/h3&gt;
&lt;p&gt;To use &lt;a class="reference external" href="https://mmonit.com/monit/"&gt;monit&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/mosquitto-mqtt-on-tinycore/#id11" id="id12"&gt;6&lt;/a&gt; to monitor Mosquitto, add these checks to &lt;code class="docutils literal"&gt;monitrc&lt;/code&gt;
(or in a separate file in &lt;code class="docutils literal"&gt;monit.d/&lt;/code&gt;):&lt;/p&gt;
&lt;pre class="literal-block"&gt;check process mosquitto pidfile /var/run/mosquitto.pid
    group mqtt
    if failed uid mosquitto then alert

check program mqtt-connect
with path "/usr/local/bin/mosquitto_sub --quiet -C 1 --id monit --topic $SYS/broker/version"
    group mqtt
    if status != 0 then alert&lt;/pre&gt;
&lt;p&gt;In order to check if the broker is running, connect to it and retrieve
one message. We request a message from the &lt;code class="docutils literal"&gt;$SYS&lt;/code&gt; hierarchy and pick
one that is &lt;em&gt;static&lt;/em&gt; so that we can be sure that it exists.
The &lt;code class="docutils literal"&gt;$SYS&lt;/code&gt; hierarchy is described in the &lt;a class="reference external" href="https://mosquitto.org/man/mosquitto-8.html#idm46187459232016"&gt;Mosquitto docs&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/mosquitto-mqtt-on-tinycore/#id13" id="id14"&gt;7&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code class="docutils literal"&gt;mosquitto_sub&lt;/code&gt; comes with the Mosquitto installation an we can use it like this:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_bb0e766f59ab4b7ba9af54552b2204eb-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; mosquitto_sub --quiet -C &lt;span class="m"&gt;1&lt;/span&gt; --id monit --topic &lt;span class="se"&gt;\$&lt;/span&gt;SYS/broker/version
&lt;/pre&gt;&lt;p&gt;This should be sufficient, i.e. exit with "0" if the connection was possible.
The &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;-C&lt;/span&gt; 1&lt;/code&gt; options is necessary to exit the program after the first message.
Note that you need to escape the "$" for the command line but not in &lt;code class="docutils literal"&gt;monitrc&lt;/code&gt;.&lt;/p&gt;
&lt;hr class="docutils"&gt;
&lt;dl class="footnote brackets"&gt;
&lt;dt class="label" id="id1"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/mosquitto-mqtt-on-tinycore/#id2"&gt;1&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="http://mosquitto.org/"&gt;http://mosquitto.org/&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id3"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/mosquitto-mqtt-on-tinycore/#id4"&gt;2&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="http://mqtt.org/"&gt;http://mqtt.org/&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id5"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/mosquitto-mqtt-on-tinycore/#id6"&gt;3&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="http://tinycorelinux.net/"&gt;http://tinycorelinux.net/&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id7"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/mosquitto-mqtt-on-tinycore/#id8"&gt;4&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="http://distro.ibiblio.org/tinycorelinux/corebook.pdf"&gt;http://distro.ibiblio.org/tinycorelinux/corebook.pdf&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id9"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/mosquitto-mqtt-on-tinycore/#id10"&gt;5&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="http://wiki.tinycorelinux.net/wiki:creating_extensions"&gt;http://wiki.tinycorelinux.net/wiki:creating_extensions&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id11"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/mosquitto-mqtt-on-tinycore/#id12"&gt;6&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="https://mmonit.com/monit/"&gt;https://mmonit.com/monit/&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id13"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/mosquitto-mqtt-on-tinycore/#id14"&gt;7&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="https://mosquitto.org/man/mosquitto-8.html#idm46187459232016"&gt;https://mosquitto.org/man/mosquitto-8.html#idm46187459232016&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/div&gt;
&lt;/div&gt;</description><category>homeserver</category><category>linux</category><category>mqtt</category><category>raspi</category><category>tinycore</category><guid>https://akeil.de/posts/mosquitto-mqtt-on-tinycore/</guid><pubDate>Sat, 14 Jan 2017 07:37:47 GMT</pubDate></item><item><title>Node-RED on TinyCore Linux</title><link>https://akeil.de/posts/node-red-on-tinycore-linux/</link><dc:creator>Alexander Keil</dc:creator><description>&lt;div class="section" id="node-red-on-tinycore-linux"&gt;
&lt;h2&gt;Node-RED on TinyCore Linux&lt;/h2&gt;
&lt;dl class="field-list simple"&gt;
&lt;dt&gt;author&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;akeil&lt;/p&gt;
&lt;/dd&gt;
&lt;dt&gt;date&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;2017-01-14&lt;/p&gt;
&lt;/dd&gt;
&lt;dt&gt;version&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;2&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;&lt;a class="reference external" href="https://nodered.org/"&gt;Node-RED&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/node-red-on-tinycore-linux/#id1" id="id2"&gt;1&lt;/a&gt; is a flow based tool which can be used to connect services and
devices with each other and to small automation flows.&lt;/p&gt;
&lt;p&gt;Node-RED runs as a &lt;a class="reference external" href="https://nodejs.org/"&gt;NodeJS&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/node-red-on-tinycore-linux/#id3" id="id4"&gt;2&lt;/a&gt; application and is managed through the browser.&lt;/p&gt;
&lt;p&gt;This post shows how to install it on a &lt;a class="reference external" href="https://www.raspberrypi.org/"&gt;Raspberry Pi&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/node-red-on-tinycore-linux/#id5" id="id6"&gt;3&lt;/a&gt;
running &lt;a class="reference external" href="http://tinycorelinux.net/"&gt;TinyCore Linux&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/node-red-on-tinycore-linux/#id7" id="id8"&gt;4&lt;/a&gt;&lt;/p&gt;
&lt;!-- TEASER_END --&gt;
&lt;div class="section" id="installation"&gt;
&lt;h3&gt;Installation&lt;/h3&gt;
&lt;p&gt;First, install &lt;em&gt;Node.js&lt;/em&gt;:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_a8d11d16ded446e89275f7aa5cd2b215-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; tce-load -w- i node
&lt;/pre&gt;&lt;p&gt;Then install Node-RED through the &lt;a class="reference external" href="https://www.npmjs.com/"&gt;Node Package Manager&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/node-red-on-tinycore-linux/#id9" id="id10"&gt;5&lt;/a&gt; (&lt;code class="docutils literal"&gt;npm&lt;/code&gt;):&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_9c6fa93307874ed19bcb735268b02453-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo npm install -g --unsafe-perm node-red
&lt;/pre&gt;&lt;div class="admonition note"&gt;
&lt;p class="admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;The &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;-g&lt;/span&gt;&lt;/code&gt; option installs it &lt;em&gt;globally&lt;/em&gt;. The recommended procedure is to
install packages which contain executable command (like Node-RED) &lt;em&gt;globally&lt;/em&gt;
and other packages &lt;em&gt;locally&lt;/em&gt; (simply without the &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;-g&lt;/span&gt;&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;The Default location for global NPM packages on TinyCore Linux is
&lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;/usr/local/lib/node_modules/&amp;lt;package&amp;gt;&lt;/span&gt;&lt;/code&gt;.
Local packages are installed in a &lt;code class="docutils literal"&gt;node_modules&lt;/code&gt; directory
inside the current working directory.&lt;/p&gt;
&lt;p&gt;The &lt;code class="docutils literal"&gt;npm install&lt;/code&gt; command will also be used to install additional
packages (flows or nodes) for Node-RED.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Unfortunately, having a custom package manager does not go well with the
philosophy of TinyCore extensions.
Packages installed through &lt;code class="docutils literal"&gt;npm&lt;/code&gt; will not be persisted and are lost on reboot.&lt;/p&gt;
&lt;p&gt;Two things can be done to prevent this:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;install packages into a location which is backed up and restored,
e.g. one of the &lt;code class="docutils literal"&gt;/home/*&lt;/code&gt; directories.
This will be the solution for additional node packages.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create tce's for every package.
We will do this for the Node-RED package only.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To create a tce for the &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;node-red&lt;/span&gt;&lt;/code&gt; package:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_c165bdea5fa54cb691867661a03f407b-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; mkdir -p /tmp/node-red/usr/local/lib/node_modules
&lt;a name="rest_code_c165bdea5fa54cb691867661a03f407b-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo mv /usr/local/lib/node_modules/node-red /tmp/node-red/usr/local/lib/node_modules
&lt;a name="rest_code_c165bdea5fa54cb691867661a03f407b-3"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; mkdir -p /tmp/node-red/usr/local/bin
&lt;a name="rest_code_c165bdea5fa54cb691867661a03f407b-4"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; /tmp/node-red/usr/local/bin
&lt;a name="rest_code_c165bdea5fa54cb691867661a03f407b-5"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; ln -s ../lib/node_modules/node-red/red.js node-red
&lt;a name="rest_code_c165bdea5fa54cb691867661a03f407b-6"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; ln -s ../lib/node_modules/node-red/bin/node-red-pi node-red-pi
&lt;a name="rest_code_c165bdea5fa54cb691867661a03f407b-7"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; ~
&lt;a name="rest_code_c165bdea5fa54cb691867661a03f407b-8"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo chown root:root -R /tmp/node-red/
&lt;a name="rest_code_c165bdea5fa54cb691867661a03f407b-9"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; tce-load -wo squashfs-tools
&lt;a name="rest_code_c165bdea5fa54cb691867661a03f407b-10"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; tce-load -il squashfs-tools
&lt;a name="rest_code_c165bdea5fa54cb691867661a03f407b-11"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; mksquashfs /tmp/node-red/ /tmp/node-red.tcz
&lt;a name="rest_code_c165bdea5fa54cb691867661a03f407b-12"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo rm -r /tmp/node-red/
&lt;a name="rest_code_c165bdea5fa54cb691867661a03f407b-13"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; mv /tmp/node-red.tcz /etc/sysconfig/tcedir/optional/
&lt;/pre&gt;&lt;p&gt;Now, after a reboot, the node-red installation should be &lt;em&gt;gone&lt;/em&gt;.
But it can now be installed with:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_c4fe139ebf1342788a79a46824658197-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; tce-load -i node-red
&lt;/pre&gt;&lt;p&gt;To make sure that Node-RED is reinstalled on boot, add it to &lt;code class="docutils literal"&gt;onboot.lst&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_a96f20df7073435f876f1410da280b9d-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; node-red.tcz &amp;gt;&amp;gt; /etc/sysconfig/tcedir/onboot.lst
&lt;/pre&gt;&lt;p&gt;Finally, we need to secure the Node-RED configuration and data files.
These are stored in &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;$HOME/.node-red&lt;/span&gt;&lt;/code&gt; and we add it to &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;/opt/.filetool.lst&lt;/span&gt;&lt;/code&gt;.
Or rather: make sure that &lt;code class="docutils literal"&gt;home&lt;/code&gt; directories are present (they are, by default).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="additional-node-packages"&gt;
&lt;h3&gt;Additional Node Packages&lt;/h3&gt;
&lt;p&gt;Additional node packages will be installed in the &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;/home/nodered/.node-red&lt;/span&gt;&lt;/code&gt;,
assuming the the &lt;code class="docutils literal"&gt;nodered&lt;/code&gt; user will run the application.
This is done like this:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_71da5d1b9499455fac98dff314cdbc92-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; /home/nodered/.node.red
&lt;a name="rest_code_71da5d1b9499455fac98dff314cdbc92-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; npm install node-red-contrib-foo
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="section" id="user"&gt;
&lt;h3&gt;User&lt;/h3&gt;
&lt;p&gt;Next, we need to create the &lt;code class="docutils literal"&gt;nodered&lt;/code&gt; user which we will use to run
the service.&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_c64a4a44343e4d12a8a81a8f217a9bc0-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo adduser -S nodered
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="section" id="start-script"&gt;
&lt;h3&gt;Start Script&lt;/h3&gt;
&lt;p&gt;To start the service at boot, edit &lt;code class="docutils literal"&gt;/opt/bootlocal.sh&lt;/code&gt;.
Since Node-RED only implements logging to the console, we redirect its
output into a log file:&lt;/p&gt;
&lt;pre class="code bash"&gt;&lt;a name="rest_code_62f67ae24e5d477d9c789f263bee9c5e-1"&gt;&lt;/a&gt;&lt;span class="c1"&gt;# start Node-RED with nodered-user in a subshell&lt;/span&gt;
&lt;a name="rest_code_62f67ae24e5d477d9c789f263bee9c5e-2"&gt;&lt;/a&gt;&lt;span class="nv"&gt;NR_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/var/log/node-red.log
&lt;a name="rest_code_62f67ae24e5d477d9c789f263bee9c5e-3"&gt;&lt;/a&gt;touch &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NR_LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;a name="rest_code_62f67ae24e5d477d9c789f263bee9c5e-4"&gt;&lt;/a&gt;chown nodered:root &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NR_LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;a name="rest_code_62f67ae24e5d477d9c789f263bee9c5e-5"&gt;&lt;/a&gt;&lt;span class="o"&gt;(&lt;/span&gt;su nodered -c /usr/local/bin/node-red -s /bin/sh &amp;gt;&amp;gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NR_LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="section" id="monitoring-with-monit"&gt;
&lt;h3&gt;Monitoring (with monit)&lt;/h3&gt;
&lt;p&gt;This is how &lt;a class="reference external" href="https://mmonit.com/monit/"&gt;monit&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/node-red-on-tinycore-linux/#id11" id="id12"&gt;6&lt;/a&gt; can be used to monitor that Node-RED is up and running.
This assumes a working installation of &lt;em&gt;monit&lt;/em&gt; on TinyCore Linux.&lt;/p&gt;
&lt;p&gt;Adding checks for Node-RED is done by editing a configuration file.
The file is located in &lt;code class="docutils literal"&gt;/usr/local/etc/monit.d&lt;/code&gt;.
Actually, monit's configuration file is &lt;code class="docutils literal"&gt;/usr/local/etc/monitrc&lt;/code&gt; but it is
possible to split the configuration into several files by adding this line to
&lt;code class="docutils literal"&gt;monitrc&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="literal-block"&gt;include /usr/local/etc/monit.d/*&lt;/pre&gt;
&lt;p&gt;So, inside &lt;code class="docutils literal"&gt;monit.d/&lt;/code&gt;, create a new file named &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;node-red&lt;/span&gt;&lt;/code&gt; and add
a check which looks for the &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;Node-red&lt;/span&gt;&lt;/code&gt; process and make a HTTP request
against the &lt;a class="reference external" href="http://nodered.org/docs/api/admin/"&gt;Node-RED Admin API&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/node-red-on-tinycore-linux/#id13" id="id14"&gt;7&lt;/a&gt;, specifically its &lt;code class="docutils literal"&gt;auth&lt;/code&gt; endpoint.
The &lt;code class="docutils literal"&gt;auth&lt;/code&gt; endpoint lists the currently active authentication scheme
and is available even if authentication is on.&lt;/p&gt;
&lt;p&gt;The endpoint should return either a JSON object with details on the
authentication scheme or an empty JSON object. In any case, a pair of
curly braces.&lt;/p&gt;
&lt;p&gt;We will also make sure that the application is running with the intended user.&lt;/p&gt;
&lt;p&gt;The check looks like this:&lt;/p&gt;
&lt;pre class="literal-block"&gt;# list authentication scheme from Admin-API - see
# http://nodered.org/docs/api/admin/oauth
check process node-red matching node-red
    group node-red
    if failed
        port 1880
        with protocol http
        request /auth/login
        content = "\{.*?\}"
    then alert

    if failed uid nodered then alert&lt;/pre&gt;
&lt;hr class="docutils"&gt;
&lt;dl class="footnote brackets"&gt;
&lt;dt class="label" id="id1"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/node-red-on-tinycore-linux/#id2"&gt;1&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="https://nodered.org/"&gt;https://nodered.org/&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id3"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/node-red-on-tinycore-linux/#id4"&gt;2&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="https://nodejs.org/"&gt;https://nodejs.org/&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id5"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/node-red-on-tinycore-linux/#id6"&gt;3&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="https://www.raspberrypi.org/"&gt;https://www.raspberrypi.org/&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id7"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/node-red-on-tinycore-linux/#id8"&gt;4&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="http://tinycorelinux.net/"&gt;http://tinycorelinux.net/&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id9"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/node-red-on-tinycore-linux/#id10"&gt;5&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="https://www.npmjs.com/"&gt;https://www.npmjs.com/&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id11"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/node-red-on-tinycore-linux/#id12"&gt;6&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="https://mmonit.com/monit/"&gt;https://mmonit.com/monit/&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id13"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/node-red-on-tinycore-linux/#id14"&gt;7&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="http://nodered.org/docs/api/admin/"&gt;http://nodered.org/docs/api/admin/&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/div&gt;
&lt;/div&gt;</description><category>homeserver</category><category>linux</category><category>nodejs</category><category>raspi</category><category>tinycore</category><guid>https://akeil.de/posts/node-red-on-tinycore-linux/</guid><pubDate>Thu, 12 Jan 2017 20:51:19 GMT</pubDate></item><item><title>Firefox Sync Server - Backup</title><link>https://akeil.de/posts/firefox-sync-server-backup/</link><dc:creator>Alexander Keil</dc:creator><description>&lt;div class="section" id="firefox-sync-server-backup"&gt;
&lt;h2&gt;Firefox Sync Server - Backup&lt;/h2&gt;
&lt;dl class="field-list simple"&gt;
&lt;dt&gt;author&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;akeil&lt;/p&gt;
&lt;/dd&gt;
&lt;dt&gt;date&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;2016-09-15&lt;/p&gt;
&lt;/dd&gt;
&lt;dt&gt;version&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;1&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;This is a follow up on a
&lt;a class="reference external" href="https://akeil.de/posts/firefox-sync-server-on-a-raspberry-pi/"&gt;previous post&lt;/a&gt;.
where I installed a &lt;em&gt;Firefox Sync Server&lt;/em&gt; on a Raspberry Pi running &lt;em&gt;piCore&lt;/em&gt;.
After having the Sync Service basically running, it is time to set up regular
backups.&lt;/p&gt;
&lt;!-- TEASER_END --&gt;
&lt;div class="section" id="filesystem-backup"&gt;
&lt;h3&gt;Filesystem Backup&lt;/h3&gt;
&lt;p&gt;This is a variant on a backup-scheme described in an
&lt;a class="reference external" href="https://akeil.de/posts/rolling-backup-with-rsync/"&gt;older post&lt;/a&gt;.
Most backups work by creating a backup locally and then pushing it to a remote
server. In this case there is no remote server and the backup is to be stored
on a desktop computer. The problem is that the desktop computer is not always
running.&lt;/p&gt;
&lt;p&gt;Therefore the backup will be initiated from the &lt;em&gt;destination&lt;/em&gt; machine.
It assumes that the &lt;em&gt;source&lt;/em&gt; machine(s) will always be reachable.&lt;/p&gt;
&lt;p&gt;TinyCore Linux already has a &lt;a class="reference external" href="http://wiki.tinycorelinux.net/wiki:backup"&gt;backup facility&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/firefox-sync-server-backup/#id1" id="id2"&gt;1&lt;/a&gt;, namely &lt;code class="docutils literal"&gt;filetool.sh&lt;/code&gt;.
This creates a backup of selected files under &lt;code class="docutils literal"&gt;mydata.tgz&lt;/code&gt;.
Data is restored on every boot.&lt;/p&gt;
&lt;p&gt;Use&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_3378b4b7079445c6835a4d6b48b0bd26-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; filetool.sh -bs
&lt;/pre&gt;&lt;p&gt;The &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;-s&lt;/span&gt;&lt;/code&gt; option tells filetool to create a safe backup.
"Safe" means that the previous backup is secured before it is overwritten
with a new one.&lt;/p&gt;
&lt;p&gt;Still, &lt;code class="docutils literal"&gt;mydata.tgz&lt;/code&gt; is stored in the &lt;code class="docutils literal"&gt;tce&lt;/code&gt; directory on a local drive.
And for a Raspberry Pi this means the SD card or an attached USB removable
drive. Not exactly reliable storage media.&lt;/p&gt;
&lt;p&gt;We will use &lt;a class="reference external" href="https://rsync.samba.org/"&gt;rsync&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/firefox-sync-server-backup/#id3" id="id4"&gt;2&lt;/a&gt; to periodically download data from the &lt;code class="docutils literal"&gt;tce&lt;/code&gt; directory
(and possibly other locations) to another computer.
The basic command should look like this:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_920d44b5f1d1450c92f2c2a5127fa012-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; rsync --archive rsync://box/&amp;lt;src&amp;gt; &amp;lt;dst&amp;gt;
&lt;/pre&gt;&lt;div class="section" id="install-and-configure-rsync"&gt;
&lt;h4&gt;Install and Configure rsync&lt;/h4&gt;
&lt;p&gt;Install &lt;em&gt;rsync&lt;/em&gt; on the TinyCore Linux machine with:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_342cc667232d4cad983ec702a4d9c14c-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; tce-load -wi rsync
&lt;/pre&gt;&lt;p&gt;Install with &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;-wi&lt;/span&gt;&lt;/code&gt; to have it added to the &lt;code class="docutils literal"&gt;onboot.lst&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We need rsync to run in &lt;em&gt;daemon&lt;/em&gt; mode so that the client can connect
to it at any time.&lt;/p&gt;
&lt;p&gt;First, create a minimal &lt;a class="reference external" href="https://download.samba.org/pub/rsync/rsyncd.conf.html"&gt;rsyncd configuration&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/firefox-sync-server-backup/#id5" id="id6"&gt;3&lt;/a&gt; at &lt;code class="docutils literal"&gt;/etc/rsyncd.conf&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="literal-block"&gt;uid = nobody
gid = nogroup
pid file = /var/run/rsyncd.pid
log file = /var/log/rsyncd.log
use chroot = no

[tce]
  comment = tce directory backup
  path = /mnt/mmcblk0p2/tce
  read only = yes
  uid = tc&lt;/pre&gt;
&lt;p&gt;The config defines a &lt;em&gt;module&lt;/em&gt; named &lt;code class="docutils literal"&gt;tce&lt;/code&gt;.
This means clients will see files under &lt;code class="docutils literal"&gt;/mnt/mmcblk0p2/tce&lt;/code&gt;
when they request &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;rsync://box/tce&lt;/span&gt;&lt;/code&gt;.
Since it is only intended for backup it is marked as &lt;em&gt;read only&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Add as many modules for other locations as required.&lt;/p&gt;
&lt;div class="admonition note"&gt;
&lt;p class="admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;Remember to add &lt;code class="docutils literal"&gt;etc/rsyncd.conf&lt;/code&gt; to &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;/opt/.filetool.lst&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Start it on boot in &lt;code class="docutils literal"&gt;bootlocal.sh&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code bash"&gt;&lt;a name="rest_code_75dbb35b0f0840e78b12aa251cc02293-1"&gt;&lt;/a&gt;&lt;span class="c1"&gt;# start rsyncd&lt;/span&gt;
&lt;a name="rest_code_75dbb35b0f0840e78b12aa251cc02293-2"&gt;&lt;/a&gt;rsync --daemon
&lt;/pre&gt;&lt;p&gt;Stop it on shutdown in &lt;code class="docutils literal"&gt;shutdown.sh&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code bash"&gt;&lt;a name="rest_code_dea70ed677ca4a79833e32ea7eadc911-1"&gt;&lt;/a&gt;&lt;span class="c1"&gt;# stop rsyncd - location of pidfile from /etc/rsyncd.conf&lt;/span&gt;
&lt;a name="rest_code_dea70ed677ca4a79833e32ea7eadc911-2"&gt;&lt;/a&gt;&lt;span class="nv"&gt;pid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;cat /var/run/rsyncd.pid&lt;span class="k"&gt;)&lt;/span&gt;
&lt;a name="rest_code_dea70ed677ca4a79833e32ea7eadc911-3"&gt;&lt;/a&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; -n &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$pid&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;a name="rest_code_dea70ed677ca4a79833e32ea7eadc911-4"&gt;&lt;/a&gt;    &lt;span class="nb"&gt;kill&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$pid&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;a name="rest_code_dea70ed677ca4a79833e32ea7eadc911-5"&gt;&lt;/a&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="section" id="the-backup-destination"&gt;
&lt;h4&gt;The Backup Destination&lt;/h4&gt;
&lt;p&gt;The actual backup task runs on the computer which is the backup &lt;em&gt;destination&lt;/em&gt;.
It will be set up so that it connects to a configured list of hosts (&lt;em&gt;sources&lt;/em&gt;)
via &lt;code class="docutils literal"&gt;rsync&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The script retrieves a list of rsync &lt;em&gt;modules&lt;/em&gt; from each host.
If a specific &lt;code class="docutils literal"&gt;KEYWORD&lt;/code&gt; (in this case: "backup") is part of the module
name or comment, all files from that module will be included in the backup.&lt;/p&gt;
&lt;p&gt;The last &lt;code class="docutils literal"&gt;KEEP&lt;/code&gt; backups are kept, the oldest backup is removed.&lt;/p&gt;
&lt;p&gt;Any host that is registered with the backup script can define any number
of rsync &lt;em&gt;modules&lt;/em&gt; and if they contain the keyword, they will be backed up.
Hosts can also use the &lt;code class="docutils literal"&gt;exclude&lt;/code&gt; option in a module definition to control
which files are backed up.&lt;/p&gt;
&lt;p&gt;On the destination this will result in a directory structure like this:&lt;/p&gt;
&lt;pre class="literal-block"&gt;BASE/
    foo/           # backups from the "foo" host
        files.0/   # the most recent backup of "files"
            [...]
        files.1/   # an older version of "files"
            [...]
        files.2/
            [...]
        data.0/
            [...]
        data.1/
            [...]
        data.2/
            [...]
    bar/
        data.0/
        data.1/&lt;/pre&gt;
&lt;p&gt;Where we have two hosts "foo" and "bar".
The "foo" hosts has two rsync modules included in the backup
and we have collected three backups of each (0..2).
The "bar" host has only one module of which two backups were collected.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="systemd-timer-and-service"&gt;
&lt;h4&gt;Systemd Timer and Service&lt;/h4&gt;
&lt;p&gt;To control the periodic backup through &lt;em&gt;systemd&lt;/em&gt;
a &lt;code class="docutils literal"&gt;.timer&lt;/code&gt; and &lt;code class="docutils literal"&gt;.service&lt;/code&gt; file are needed.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;timer&lt;/em&gt; goes into &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;/etc/systemd/system/pi-backup.timer&lt;/span&gt;&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code ini"&gt;&lt;a name="rest_code_e106958f9def43ffb242e23c6b597d06-1"&gt;&lt;/a&gt;&lt;span class="k"&gt;[Unit]&lt;/span&gt;
&lt;a name="rest_code_e106958f9def43ffb242e23c6b597d06-2"&gt;&lt;/a&gt;&lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Timer for Rasperry Pi backup&lt;/span&gt;
&lt;a name="rest_code_e106958f9def43ffb242e23c6b597d06-3"&gt;&lt;/a&gt;
&lt;a name="rest_code_e106958f9def43ffb242e23c6b597d06-4"&gt;&lt;/a&gt;&lt;span class="k"&gt;[Timer]&lt;/span&gt;
&lt;a name="rest_code_e106958f9def43ffb242e23c6b597d06-5"&gt;&lt;/a&gt;&lt;span class="na"&gt;OnCalendar&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;daily&lt;/span&gt;
&lt;a name="rest_code_e106958f9def43ffb242e23c6b597d06-6"&gt;&lt;/a&gt;&lt;span class="na"&gt;Persistent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;a name="rest_code_e106958f9def43ffb242e23c6b597d06-7"&gt;&lt;/a&gt;
&lt;a name="rest_code_e106958f9def43ffb242e23c6b597d06-8"&gt;&lt;/a&gt;&lt;span class="k"&gt;[Install]&lt;/span&gt;
&lt;a name="rest_code_e106958f9def43ffb242e23c6b597d06-9"&gt;&lt;/a&gt;&lt;span class="na"&gt;WantedBy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;default.target&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;And the &lt;em&gt;service&lt;/em&gt; in &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;/etc/systemd/system/pi-backup.service&lt;/span&gt;&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code ini"&gt;&lt;a name="rest_code_a970140f17ee4457ba8e2d769fbcd2b9-1"&gt;&lt;/a&gt;&lt;span class="k"&gt;[Unit]&lt;/span&gt;
&lt;a name="rest_code_a970140f17ee4457ba8e2d769fbcd2b9-2"&gt;&lt;/a&gt;&lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Backup for Raspberry Pis&lt;/span&gt;
&lt;a name="rest_code_a970140f17ee4457ba8e2d769fbcd2b9-3"&gt;&lt;/a&gt;
&lt;a name="rest_code_a970140f17ee4457ba8e2d769fbcd2b9-4"&gt;&lt;/a&gt;&lt;span class="k"&gt;[Service]&lt;/span&gt;
&lt;a name="rest_code_a970140f17ee4457ba8e2d769fbcd2b9-5"&gt;&lt;/a&gt;&lt;span class="na"&gt;ExecStart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/usr/local/bin/pi-backup.sh&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Assuming that the backup script is located at
&lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;/usr/local/bin/pi-backup.sh&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The timer is activated with:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_dd2cc9d2c5f64b10a4e82ddd529f1a8a-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; pi-backup.timer
&lt;/pre&gt;&lt;p&gt;Backups can be started manually with:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_3da35880efc744aeb751a039725bdbc5-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo systemctl start pi-backup.service
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="section" id="backup-script"&gt;
&lt;h4&gt;Backup Script&lt;/h4&gt;
&lt;p&gt;The complete script looks like this.
The UPPERCASE variables at the top of the script are meant for
configuration:&lt;/p&gt;
&lt;pre class="code bash"&gt;&lt;a name="rest_code_7388126b959f44659d947c14049e6630-1"&gt;&lt;/a&gt;&lt;span class="ch"&gt;#!/bin/sh&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-2"&gt;&lt;/a&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-3"&gt;&lt;/a&gt;&lt;span class="c1"&gt;# basedir for all backups&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-4"&gt;&lt;/a&gt;&lt;span class="nv"&gt;ROOT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/path/to/backups
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-5"&gt;&lt;/a&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-6"&gt;&lt;/a&gt;&lt;span class="c1"&gt;# hosts to consider for backup&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-7"&gt;&lt;/a&gt;&lt;span class="nv"&gt;HOSTS&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt; foo bar &lt;span class="o"&gt;)&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-8"&gt;&lt;/a&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-9"&gt;&lt;/a&gt;&lt;span class="c1"&gt;# modules with KEYWORD in name or description are backed up&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-10"&gt;&lt;/a&gt;&lt;span class="nv"&gt;KEYWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;backup
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-11"&gt;&lt;/a&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-12"&gt;&lt;/a&gt;&lt;span class="c1"&gt;# the number of old backups to keep (not including the current backup)&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-13"&gt;&lt;/a&gt;&lt;span class="nv"&gt;KEEP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-14"&gt;&lt;/a&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-15"&gt;&lt;/a&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-16"&gt;&lt;/a&gt;&lt;span class="k"&gt;function&lt;/span&gt; main &lt;span class="o"&gt;{&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-17"&gt;&lt;/a&gt;    &lt;span class="k"&gt;for&lt;/span&gt; host in &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOSTS&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-18"&gt;&lt;/a&gt;        backup_host &lt;span class="nv"&gt;$host&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-19"&gt;&lt;/a&gt;    &lt;span class="k"&gt;done&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-20"&gt;&lt;/a&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-21"&gt;&lt;/a&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-22"&gt;&lt;/a&gt;&lt;span class="c1"&gt;# fetch a list of modules&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-23"&gt;&lt;/a&gt;&lt;span class="c1"&gt;# filter all modules with "backup" in their description&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-24"&gt;&lt;/a&gt;&lt;span class="c1"&gt;# args: host&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-25"&gt;&lt;/a&gt;&lt;span class="k"&gt;function&lt;/span&gt; backup_host &lt;span class="o"&gt;{&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-26"&gt;&lt;/a&gt;    &lt;span class="nb"&gt;local&lt;/span&gt; &lt;span class="nv"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-27"&gt;&lt;/a&gt;    &lt;span class="c1"&gt;# get a list of all modules for that host,&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-28"&gt;&lt;/a&gt;    &lt;span class="c1"&gt;# filter the ones containing "KEYWORD"&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-29"&gt;&lt;/a&gt;    &lt;span class="c1"&gt;# and keep only the module name&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-30"&gt;&lt;/a&gt;    rsync rsync://&lt;span class="nv"&gt;$host&lt;/span&gt;/ &lt;span class="p"&gt;|&lt;/span&gt; grep &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$KEYWORD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; cut -f1 &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; -r module&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-31"&gt;&lt;/a&gt;        backup_module &lt;span class="nv"&gt;$host&lt;/span&gt; &lt;span class="nv"&gt;$module&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-32"&gt;&lt;/a&gt;    &lt;span class="k"&gt;done&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-33"&gt;&lt;/a&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-34"&gt;&lt;/a&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-35"&gt;&lt;/a&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-36"&gt;&lt;/a&gt;&lt;span class="c1"&gt;# backup a single module&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-37"&gt;&lt;/a&gt;&lt;span class="c1"&gt;# args: host module&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-38"&gt;&lt;/a&gt;&lt;span class="k"&gt;function&lt;/span&gt; backup_module &lt;span class="o"&gt;{&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-39"&gt;&lt;/a&gt;    &lt;span class="nb"&gt;local&lt;/span&gt; &lt;span class="nv"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-40"&gt;&lt;/a&gt;    &lt;span class="nb"&gt;local&lt;/span&gt; &lt;span class="nv"&gt;module&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-41"&gt;&lt;/a&gt;    &lt;span class="nb"&gt;local&lt;/span&gt; &lt;span class="nv"&gt;basedst&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$ROOT&lt;/span&gt;/&lt;span class="nv"&gt;$host&lt;/span&gt;/&lt;span class="nv"&gt;$module&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-42"&gt;&lt;/a&gt;    &lt;span class="nb"&gt;local&lt;/span&gt; &lt;span class="nv"&gt;dst&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$basedst&lt;/span&gt;.0
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-43"&gt;&lt;/a&gt;    &lt;span class="nb"&gt;local&lt;/span&gt; &lt;span class="nv"&gt;lndst&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;../&lt;span class="nv"&gt;$module&lt;/span&gt;.1
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-44"&gt;&lt;/a&gt;    rotate &lt;span class="nv"&gt;$basedst&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-45"&gt;&lt;/a&gt;    &lt;span class="nb"&gt;echo&lt;/span&gt; Backup &lt;span class="nv"&gt;$host&lt;/span&gt;/&lt;span class="nv"&gt;$module&lt;/span&gt; to &lt;span class="nv"&gt;$dst&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-46"&gt;&lt;/a&gt;    mkdir -p &lt;span class="nv"&gt;$dst&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-47"&gt;&lt;/a&gt;    rsync --archive --delete --link-dest&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$lndst&lt;/span&gt; rsync://&lt;span class="nv"&gt;$host&lt;/span&gt;/&lt;span class="nv"&gt;$module&lt;/span&gt; &lt;span class="nv"&gt;$dst&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-48"&gt;&lt;/a&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-49"&gt;&lt;/a&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-50"&gt;&lt;/a&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-51"&gt;&lt;/a&gt;&lt;span class="c1"&gt;# drop the oldest backup,&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-52"&gt;&lt;/a&gt;&lt;span class="c1"&gt;# move other backups up one place (e.g. `backup.0` to `backup.1`)&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-53"&gt;&lt;/a&gt;&lt;span class="c1"&gt;# args: basedst&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-54"&gt;&lt;/a&gt;&lt;span class="k"&gt;function&lt;/span&gt; rotate &lt;span class="o"&gt;{&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-55"&gt;&lt;/a&gt;    &lt;span class="nb"&gt;local&lt;/span&gt; &lt;span class="nv"&gt;counter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$KEEP&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-56"&gt;&lt;/a&gt;    &lt;span class="nb"&gt;local&lt;/span&gt; &lt;span class="nv"&gt;dst&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-57"&gt;&lt;/a&gt;    &lt;span class="c1"&gt;# going backwards: n, n-1, ... 0&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-58"&gt;&lt;/a&gt;    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$counter&lt;/span&gt; -ge &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-59"&gt;&lt;/a&gt;        &lt;span class="nv"&gt;ahead&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="nv"&gt;$counter&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-60"&gt;&lt;/a&gt;        &lt;span class="nb"&gt;local&lt;/span&gt; &lt;span class="nv"&gt;current&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$dst&lt;/span&gt;.&lt;span class="nv"&gt;$counter&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-61"&gt;&lt;/a&gt;        &lt;span class="nb"&gt;local&lt;/span&gt; &lt;span class="nv"&gt;older&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$dst&lt;/span&gt;.&lt;span class="nv"&gt;$ahead&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-62"&gt;&lt;/a&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-63"&gt;&lt;/a&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; -d &lt;span class="nv"&gt;$current&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-64"&gt;&lt;/a&gt;            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$counter&lt;/span&gt; -eq &lt;span class="nv"&gt;$KEEP&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-65"&gt;&lt;/a&gt;                &lt;span class="nb"&gt;echo&lt;/span&gt; delete &lt;span class="nv"&gt;$current&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-66"&gt;&lt;/a&gt;                rm -r &lt;span class="nv"&gt;$current&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-67"&gt;&lt;/a&gt;            &lt;span class="k"&gt;else&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-68"&gt;&lt;/a&gt;                &lt;span class="nb"&gt;echo&lt;/span&gt; move &lt;span class="nv"&gt;$current&lt;/span&gt; to &lt;span class="nv"&gt;$older&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-69"&gt;&lt;/a&gt;                mv &lt;span class="nv"&gt;$current&lt;/span&gt; &lt;span class="nv"&gt;$older&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-70"&gt;&lt;/a&gt;            &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-71"&gt;&lt;/a&gt;        &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-72"&gt;&lt;/a&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-73"&gt;&lt;/a&gt;        &lt;span class="nv"&gt;counter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="nv"&gt;$counter&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-74"&gt;&lt;/a&gt;    &lt;span class="k"&gt;done&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-75"&gt;&lt;/a&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-76"&gt;&lt;/a&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-77"&gt;&lt;/a&gt;&lt;span class="c1"&gt;# run it&lt;/span&gt;
&lt;a name="rest_code_7388126b959f44659d947c14049e6630-78"&gt;&lt;/a&gt;main
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="sql-dumps"&gt;
&lt;h3&gt;SQL Dumps&lt;/h3&gt;
&lt;p&gt;The Firefox sync server was installed using &lt;em&gt;MariaDB&lt;/em&gt; as a database backend.
We will perform &lt;em&gt;logical&lt;/em&gt; backups of the database.
That is, the backup takes the form of SQL statements which,
when executed, restore the original data.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="https://mariadb.com/kb/en/mariadb/mysqldump/"&gt;mysqldump&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/firefox-sync-server-backup/#id7" id="id8"&gt;4&lt;/a&gt; is used to dump the complete database into SQL statements.&lt;/p&gt;
&lt;p&gt;The backup script for the &lt;em&gt;ffsync&lt;/em&gt; database looks like this:&lt;/p&gt;
&lt;pre class="code bash"&gt;&lt;a name="rest_code_daa3deb027c74473959921ffee6c782b-1"&gt;&lt;/a&gt;&lt;span class="ch"&gt;#!/bin/sh&lt;/span&gt;
&lt;a name="rest_code_daa3deb027c74473959921ffee6c782b-2"&gt;&lt;/a&gt;&lt;span class="nv"&gt;dst&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/mnt/storage/mysql/dumps/ffsync.dump
&lt;a name="rest_code_daa3deb027c74473959921ffee6c782b-3"&gt;&lt;/a&gt;&lt;span class="nv"&gt;cfg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/opt/ffsync.db.cnf
&lt;a name="rest_code_daa3deb027c74473959921ffee6c782b-4"&gt;&lt;/a&gt;
&lt;a name="rest_code_daa3deb027c74473959921ffee6c782b-5"&gt;&lt;/a&gt;mysqldump --defaults-extra-file&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$cfg&lt;/span&gt; --lock-tables ffsync &amp;gt; &lt;span class="nv"&gt;$dst&lt;/span&gt;.temp
&lt;a name="rest_code_daa3deb027c74473959921ffee6c782b-6"&gt;&lt;/a&gt;
&lt;a name="rest_code_daa3deb027c74473959921ffee6c782b-7"&gt;&lt;/a&gt;&lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$?&lt;/span&gt;
&lt;a name="rest_code_daa3deb027c74473959921ffee6c782b-8"&gt;&lt;/a&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$status&lt;/span&gt; -eq &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;a name="rest_code_daa3deb027c74473959921ffee6c782b-9"&gt;&lt;/a&gt;    mv &lt;span class="nv"&gt;$dst&lt;/span&gt;.temp &lt;span class="nv"&gt;$dst&lt;/span&gt;
&lt;a name="rest_code_daa3deb027c74473959921ffee6c782b-10"&gt;&lt;/a&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;a name="rest_code_daa3deb027c74473959921ffee6c782b-11"&gt;&lt;/a&gt;
&lt;a name="rest_code_daa3deb027c74473959921ffee6c782b-12"&gt;&lt;/a&gt;&lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="nv"&gt;$status&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;The &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;--lock-tables&lt;/span&gt;&lt;/code&gt; option ensures that we retrieve a consistent
state of the database.
For InnoDB table types, &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;--single-transaction&lt;/span&gt;&lt;/code&gt; is recommended to achieve that
but table locking will work with any storage engine.&lt;/p&gt;
&lt;p&gt;To avoid having the database password in the command line,
a configuration file is used to keep credentials.
It is passed with &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;--defaults-extra-file&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The config file looks like this:&lt;/p&gt;
&lt;pre class="code ini"&gt;&lt;a name="rest_code_0fdb225e3acf48889d00561f23ee239f-1"&gt;&lt;/a&gt;&lt;span class="k"&gt;[mysqldump]&lt;/span&gt;
&lt;a name="rest_code_0fdb225e3acf48889d00561f23ee239f-2"&gt;&lt;/a&gt;&lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;ffsync&lt;/span&gt;
&lt;a name="rest_code_0fdb225e3acf48889d00561f23ee239f-3"&gt;&lt;/a&gt;&lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;secret&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Set the permissions for the config file so that only the owner can read it
and make it owned by the &lt;code class="docutils literal"&gt;mysql&lt;/code&gt; user.&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_069f181903584ccf8632ed3c0177023e-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo chown mysql:nogroup /opt/ffsync.db.cnf
&lt;a name="rest_code_069f181903584ccf8632ed3c0177023e-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo chmod &lt;span class="m"&gt;600&lt;/span&gt; /opt/ffsync.db.cnf
&lt;/pre&gt;&lt;p&gt;Both, script and config file are kept in &lt;code class="docutils literal"&gt;/opt&lt;/code&gt; which means they
should already be included in &lt;code class="docutils literal"&gt;filetool.lst&lt;/code&gt; for backup and restore.&lt;/p&gt;
&lt;p&gt;Finally, add the script to mysql's crontab:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_ce0d411306d94853bd49f23d7bacb992-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo crontab -eu mysql
&lt;/pre&gt;&lt;pre class="literal-block"&gt;12 1 * * * /opt/backup-ffsync-db.sh&lt;/pre&gt;
&lt;p&gt;The backup will be executed with the user we created to run the &lt;code class="docutils literal"&gt;mysqld&lt;/code&gt;
service (&lt;code class="docutils literal"&gt;mysql&lt;/code&gt;) so make sure that the backup destination is writable
for that user.&lt;/p&gt;
&lt;p&gt;To include the SQL dump in the filesystem backup,
create an rsync &lt;em&gt;module&lt;/em&gt; for it in &lt;code class="docutils literal"&gt;rsyncd.conf&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="literal-block"&gt;...
[sqldumps]
  comment = SQL dumps backup
  path = /mnt/storage/mysql/dumps
  read only = yes
  uid = mysql&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="cron"&gt;
&lt;h3&gt;Cron&lt;/h3&gt;
&lt;p&gt;We have made a &lt;code class="docutils literal"&gt;crontab&lt;/code&gt; entry but cron might not be enabled.
To enable it, we must add the &lt;a class="reference external" href="http://wiki.tinycorelinux.net/wiki:boot_codes_explained"&gt;Tiny Core bootcode&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/firefox-sync-server-backup/#id9" id="id10"&gt;5&lt;/a&gt; "&lt;code class="docutils literal"&gt;cron&lt;/code&gt;".&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Bootcodes&lt;/em&gt; can be set in a file named &lt;code class="docutils literal"&gt;cmdline.txt&lt;/code&gt;.
the file is located in the boot partition, which is normally
unmounted after boot. Mount it and edit the file:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_36453ce4bd1b418ead4613a157eb64ae-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo mount /dev/mmcblk0p1 /mnt/mmcblk0p1
&lt;a name="rest_code_36453ce4bd1b418ead4613a157eb64ae-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo vi /mnt/mmcblk0p1/cmdline.txt
&lt;a name="rest_code_36453ce4bd1b418ead4613a157eb64ae-3"&gt;&lt;/a&gt;&lt;span class="gp gp-VirtualEnv"&gt;(append bootcode "cron")&lt;/span&gt;
&lt;a name="rest_code_36453ce4bd1b418ead4613a157eb64ae-4"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo umount /mnt/mmcblk0p1
&lt;/pre&gt;&lt;p&gt;cmdline.txt contains a space separated list.
Simply append " cron" to the end of that list.&lt;/p&gt;
&lt;div class="admonition warning"&gt;
&lt;p class="admonition-title"&gt;Warning&lt;/p&gt;
&lt;p&gt;With &lt;em&gt;piCore&lt;/em&gt; 8.x and Raspberry Pi 3,
the file is &lt;code class="docutils literal"&gt;cmdline3.txt&lt;/code&gt;, &lt;em&gt;not&lt;/em&gt; &lt;code class="docutils literal"&gt;cmdline.txt&lt;/code&gt;.
See &lt;a class="reference external" href="http://forum.tinycorelinux.net/index.php?topic=20107.0"&gt;http://forum.tinycorelinux.net/index.php?topic=20107.0&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;After a reboot, check if it worked.
This should return the PID of the running &lt;code class="docutils literal"&gt;crond&lt;/code&gt; daemon:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_7e236664230245b3aab8a218fa7e5346-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; pgrep crond
&lt;/pre&gt;&lt;p&gt;Now make sure that &lt;em&gt;crontabs&lt;/em&gt; are persisted across boots.
Add the crontabs directory to &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;/opt/.filetool.lst&lt;/span&gt;&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="literal-block"&gt;var/spool/cron/crontabs&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="timezone"&gt;
&lt;h3&gt;Timezone&lt;/h3&gt;
&lt;p&gt;If you did not set it, piCore's time zone is UTC.
And your crontab entry would refer to UTC as well.&lt;/p&gt;
&lt;p&gt;This is a good opportunity to add another bootcode (&lt;code class="docutils literal"&gt;tz&lt;/code&gt;)
for setting the timezone.&lt;/p&gt;
&lt;p&gt;The &lt;a class="reference external" href="http://wiki.tinycorelinux.net/wiki:time_zone"&gt;TZ bootcode&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/firefox-sync-server-backup/#id11" id="id12"&gt;6&lt;/a&gt; is a bit more involved it
(not as complicated as it looks, though).
For &lt;em&gt;Central European Time&lt;/em&gt; the timezone is encoded like this:&lt;/p&gt;
&lt;pre class="literal-block"&gt;tz=CET-1CEST,M3.5.0,M10.5.0/3
   |  | |    || | | ||  | | `- 03:00:00
   |  | |    || | | ||  | `--- Sunday (day 0)
   |  | |    || | | ||  `----- Last week (week 5)
   |  | |    || | | |`-------- October (month 10)
   |  | |    || | | `--------- End of DST in format m.w.d[/h]
   |  | |    || | `----------- Sunday (day 0)
   |  | |    || `------------- Last week (week 5)
   |  | |    |`--------------- March (month 3)
   |  | |    `---------------- Begin of DST in format m.w.d[/h]
   |  | `--------------------- TZ name during DST
   |  `----------------------- UTC offset w/o DST
   `-------------------------- TZ name w/o DST&lt;/pre&gt;
&lt;p&gt;Start and end times for DST are specified in the format:&lt;/p&gt;
&lt;pre class="literal-block"&gt;Mm.w.d[/h]
 ^ ^ ^  ^
m: month, 1=January
w: week of the month (1=first, 5=last)
d: day of the week, 0=Sunday, 6=Monday
h: [optional] hour of the day, 3 for 03:00:00&lt;/pre&gt;
&lt;p&gt;If no &lt;em&gt;time&lt;/em&gt; parameter is given for DST start or end time,
02:00:00 is assumed.&lt;/p&gt;
&lt;p&gt;Add the parameter to &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;cmdline[3].txt&lt;/span&gt;&lt;/code&gt; (for CET):&lt;/p&gt;
&lt;pre class="literal-block"&gt;tz=CET-1CEST,M3.5.0,M10.5.0/3&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;Whether setting the timezone this way (or setting it at all)
is a good idea is debatable. For me - living in a country with
daylight savings time - it felt more comfortable having the same
time on all computers.&lt;/em&gt;&lt;/p&gt;
&lt;hr class="docutils"&gt;
&lt;dl class="footnote brackets"&gt;
&lt;dt class="label" id="id1"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/firefox-sync-server-backup/#id2"&gt;1&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="http://wiki.tinycorelinux.net/wiki:backup"&gt;http://wiki.tinycorelinux.net/wiki:backup&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id3"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/firefox-sync-server-backup/#id4"&gt;2&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="https://rsync.samba.org/"&gt;https://rsync.samba.org/&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id5"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/firefox-sync-server-backup/#id6"&gt;3&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="https://download.samba.org/pub/rsync/rsyncd.conf.html"&gt;https://download.samba.org/pub/rsync/rsyncd.conf.html&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id7"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/firefox-sync-server-backup/#id8"&gt;4&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="https://mariadb.com/kb/en/mariadb/mysqldump/"&gt;https://mariadb.com/kb/en/mariadb/mysqldump/&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id9"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/firefox-sync-server-backup/#id10"&gt;5&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="http://wiki.tinycorelinux.net/wiki:boot_codes_explained"&gt;http://wiki.tinycorelinux.net/wiki:boot_codes_explained&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id11"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/firefox-sync-server-backup/#id12"&gt;6&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="http://wiki.tinycorelinux.net/wiki:time_zone"&gt;http://wiki.tinycorelinux.net/wiki:time_zone&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/div&gt;
&lt;/div&gt;</description><category>homeserver</category><category>linux</category><category>raspi</category><category>tinycore</category><guid>https://akeil.de/posts/firefox-sync-server-backup/</guid><pubDate>Thu, 15 Sep 2016 21:45:00 GMT</pubDate></item><item><title>Firefox Sync Server on a Raspberry Pi</title><link>https://akeil.de/posts/firefox-sync-server-on-a-raspberry-pi/</link><dc:creator>Alexander Keil</dc:creator><description>&lt;div class="section" id="firefox-sync-server-on-a-raspberry-pi"&gt;
&lt;h2&gt;Firefox Sync Server on a Raspberry Pi&lt;/h2&gt;
&lt;dl class="field-list simple"&gt;
&lt;dt&gt;author&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;akeil&lt;/p&gt;
&lt;/dd&gt;
&lt;dt&gt;date&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;2016-09-14&lt;/p&gt;
&lt;/dd&gt;
&lt;dt&gt;version&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;1.2&lt;/p&gt;
&lt;/dd&gt;
&lt;dt&gt;updated&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;2016-09-15&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;The Firefox browser can use a &lt;em&gt;Sync Service&lt;/em&gt; to synchronize settings,
bookmarks and other stuff across multiple Firefox installations.
By default, Mozilla's public sync server is used but it is possible to
&lt;a class="reference external" href="https://docs.services.mozilla.com/howtos/run-sync-1.5.html"&gt;run your own sync server&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/firefox-sync-server-on-a-raspberry-pi/#id1" id="id2"&gt;1&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We will install piCore Linux and the Firefox Sync Server on a new Raspberry Pi.&lt;/p&gt;
&lt;!-- TEASER_END --&gt;
&lt;div class="section" id="install-picore-os"&gt;
&lt;h3&gt;Install piCore OS&lt;/h3&gt;
&lt;p&gt;We will use &lt;a class="reference external" href="http://tinycorelinux.net/8.x/armv6/releases/RPi/README"&gt;piCore&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/firefox-sync-server-on-a-raspberry-pi/#id3" id="id4"&gt;2&lt;/a&gt;, the Raspberry Pi version of &lt;a class="reference external" href="http://tinycorelinux.net/"&gt;Tiny Core Linux&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/firefox-sync-server-on-a-raspberry-pi/#id5" id="id6"&gt;3&lt;/a&gt;.
The benefit of using piCore is that it runs entirely from memory
which should reduce strain on the SD card.&lt;/p&gt;
&lt;p&gt;piCore is installed by downloading the image and putting it on an SD card.&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_be9769bc0b614a9886b739f409b6d11b-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; wget http://tinycorelinux.net/8.x/armv6/releases/RPi/piCore-8.0.zip
&lt;a name="rest_code_be9769bc0b614a9886b739f409b6d11b-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; unzip piCore-8.0.zip
&lt;a name="rest_code_be9769bc0b614a9886b739f409b6d11b-3"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo dd &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;piCore-8.0.img &lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/sdX &lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1M
&lt;a name="rest_code_be9769bc0b614a9886b739f409b6d11b-4"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo sync
&lt;/pre&gt;&lt;p&gt;Where &lt;code class="docutils literal"&gt;/dev/sdX&lt;/code&gt; is the empty SD card.
Don't forget to unmount all partitions from the SD card before writing to it.&lt;/p&gt;
&lt;p&gt;After that, put the SD card into the Raspberry Pi, plug in power and network
and log in with user &lt;code class="docutils literal"&gt;tc&lt;/code&gt; and password &lt;code class="docutils literal"&gt;piCore&lt;/code&gt;.&lt;/p&gt;
&lt;div class="section" id="extend-partition"&gt;
&lt;h4&gt;Extend Partition&lt;/h4&gt;
&lt;p&gt;TinyCore Linux knows two modes - "Cloud Mode" and "Mounted Mode".
Mounted Mode allows to have some persistence, e.g. for installed applications.
For this, a second partition is used.
The second partition is already available after installation but it should be
enlarged.&lt;/p&gt;
&lt;p&gt;Use &lt;code class="docutils literal"&gt;fdisk&lt;/code&gt; to delete the existing partition and create a new, larger one:&lt;/p&gt;
&lt;p&gt;The second partition will normally be mounted. &lt;strong&gt;Unmount&lt;/strong&gt; it first.&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_fdf6646edc0e4a3ab56621af1aedac21-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo umount /dev/mmcblk0p2
&lt;a name="rest_code_fdf6646edc0e4a3ab56621af1aedac21-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo fdisk -u /dev/mmcblk0
&lt;/pre&gt;&lt;p&gt;List partitions with &lt;code class="docutils literal"&gt;p&lt;/code&gt; and note down the start and end sectors of the
second partition.
Delete the second partition with &lt;code class="docutils literal"&gt;d&lt;/code&gt;, then create a new one with &lt;code class="docutils literal"&gt;n&lt;/code&gt;.
Use the same start sector as previously but different end sector.
The default end sector uses all available space.
Save with &lt;code class="docutils literal"&gt;w&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;After a reboot, log in again an expand the filesystem to fill up the second
partition:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_765faf3bc5b64addb0d3e4f7babed2b4-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo reboot
&lt;a name="rest_code_765faf3bc5b64addb0d3e4f7babed2b4-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo resize2fs /dev/mmcblk0p2
&lt;/pre&gt;&lt;p&gt;This should leave us with two partitions on the SD card.
A small partition 1 (&lt;code class="docutils literal"&gt;mmcblk0p1&lt;/code&gt;) which holds the OS files and will be
unmounted after boot and a larger partition 2 (&lt;code class="docutils literal"&gt;mmcblk0p2&lt;/code&gt;) which takes
most of the SD cards capacity and holds installed applications and data.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="add-additional-storage"&gt;
&lt;h4&gt;Add Additional Storage&lt;/h4&gt;
&lt;p&gt;We will use the No. 2 partition to persist applications and settings on the
SD card. We will also set up a removable USB drive to hold bulk data.
We create a filesystem on it and set it up to be mounted at boot.&lt;/p&gt;
&lt;p&gt;Plug the USB storage into the Raspberry Pi.
It should show up in &lt;code class="docutils literal"&gt;/proc/partitions&lt;/code&gt;,
&lt;code class="docutils literal"&gt;blkid&lt;/code&gt; can be used to get more info.&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_ece7c7dfdc8c4db2a309d6170a031350-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; cat /proc/partitions
&lt;a name="rest_code_ece7c7dfdc8c4db2a309d6170a031350-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; blkid /dev/sdX
&lt;/pre&gt;&lt;p&gt;We will put an &lt;code class="docutils literal"&gt;ext2&lt;/code&gt; filesystem on it. &lt;code class="docutils literal"&gt;ext2&lt;/code&gt; is non-journaling which
should generate less writes and &lt;a class="reference external" href="http://wiki.tinycorelinux.net/wiki:usb_life"&gt;extend the life of the flash drive&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;First, we create a single partition for the whole disk:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_aa9d03280a1c400c9381c2b38325864d-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo fdisk /dev/sdX
&lt;a name="rest_code_aa9d03280a1c400c9381c2b38325864d-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt; d
&lt;a name="rest_code_aa9d03280a1c400c9381c2b38325864d-3"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt; n
&lt;a name="rest_code_aa9d03280a1c400c9381c2b38325864d-4"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt; p
&lt;a name="rest_code_aa9d03280a1c400c9381c2b38325864d-5"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;a name="rest_code_aa9d03280a1c400c9381c2b38325864d-6"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt; w
&lt;/pre&gt;&lt;p&gt;Then, &lt;a class="reference external" href="http://www.tldp.org/HOWTO/Flash-Memory-HOWTO/ext2.html"&gt;create an ext2 filesystem&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/firefox-sync-server-on-a-raspberry-pi/#id7" id="id8"&gt;4&lt;/a&gt;:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_98f69acb97da4522be9bb637fc209289-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; mke2fs /dev/sdX1
&lt;/pre&gt;&lt;p&gt;Since the device is a plugged in drive, we mount it by its UUID.
Find the UUID with &lt;code class="docutils literal"&gt;blkid /dev/sdX1&lt;/code&gt;.
Also we choose a different directory name for the mountpoint.&lt;/p&gt;
&lt;p&gt;To mount it automatically on boot, add the following lines
to &lt;code class="docutils literal"&gt;/opt/bootlocal.sh&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="literal-block"&gt;mkdir /mnt/storage
chmod 777 /mnt/storage
mount UUID=b0ee2fe8-c54a-4c67-9a40-a9c2c4cb734c /mnt/storage&lt;/pre&gt;
&lt;p&gt;And the respective unmount command to &lt;code class="docutils literal"&gt;/opt/shutdown.sh&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="literal-block"&gt;umount /mnt/storage&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="tiny-core-persistence"&gt;
&lt;h4&gt;Tiny Core Persistence&lt;/h4&gt;
&lt;p&gt;By default, TinyCore Linux has no persistence. That means all changes made
in configuration files or newly installed applications are lost when
the system reboots.
There are two options to keep data between two boots:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;backup&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;persistent partitions&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Software packages for TinyCore Linux are called "extensions" and they are stored
in the &lt;code class="docutils literal"&gt;/tce&lt;/code&gt; directory.&lt;/p&gt;
&lt;p&gt;The preconfigured image already comes with the &lt;code class="docutils literal"&gt;/tce&lt;/code&gt; directory on the second
partition so there is not much to do here.&lt;/p&gt;
&lt;p&gt;Verify that &lt;code class="docutils literal"&gt;/mnt/mmcblk0p2/tce&lt;/code&gt; exists; it should contain some preinstalled
applications (like openssh).
Look at &lt;code class="docutils literal"&gt;/etc/fstab&lt;/code&gt; to see how &lt;code class="docutils literal"&gt;/dev/mmcblk0p2&lt;/code&gt; is mounted.&lt;/p&gt;
&lt;p&gt;The backup utility compresses selected files or directories and stores them
as &lt;code class="docutils literal"&gt;mydata.tgz&lt;/code&gt; inside the &lt;code class="docutils literal"&gt;/tce&lt;/code&gt; directory.&lt;/p&gt;
&lt;p&gt;The file &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;/opt/.filetool.lst&lt;/span&gt;&lt;/code&gt; defines which files or directories will be
included. &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;/opt/.xfiletool.lst&lt;/span&gt;&lt;/code&gt; can be used to exclude previously included
files (e.g. include a complete directory tree but exclude single large files).&lt;/p&gt;
&lt;p&gt;The default list includes the &lt;code class="docutils literal"&gt;/opt&lt;/code&gt; and &lt;code class="docutils literal"&gt;/home&lt;/code&gt; directories, ssh host keys
and files related to user management (&lt;code class="docutils literal"&gt;/etc/passwd&lt;/code&gt; for example).&lt;/p&gt;
&lt;div class="admonition warning"&gt;
&lt;p class="admonition-title"&gt;Warning&lt;/p&gt;
&lt;p&gt;The backup script does &lt;strong&gt;not&lt;/strong&gt; run automatically.
One must include a call to &lt;code class="docutils literal"&gt;filetool.sh &lt;span class="pre"&gt;-b&lt;/span&gt;&lt;/code&gt; in &lt;code class="docutils literal"&gt;/opt/shutdown.sh&lt;/code&gt;.
Also, &lt;code class="docutils literal"&gt;shutdown.sh&lt;/code&gt; will only run when the system is shut down
with &lt;code class="docutils literal"&gt;exitcheck.sh&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="install-firefox-sync"&gt;
&lt;h3&gt;Install Firefox Sync&lt;/h3&gt;
&lt;p&gt;The server is installed into a python &lt;a class="reference external" href="https://virtualenv.pypa.io/en/stable/"&gt;virtualenv&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/firefox-sync-server-on-a-raspberry-pi/#id9" id="id10"&gt;5&lt;/a&gt;
which is located in the syncserver installation directory.
The server will later run as a &lt;a class="reference external" href="https://trypyramid.com/"&gt;Pyramid&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/firefox-sync-server-on-a-raspberry-pi/#id11" id="id12"&gt;6&lt;/a&gt; app
inside a &lt;a class="reference external" href="http://gunicorn.org/"&gt;Gunicorn&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/firefox-sync-server-on-a-raspberry-pi/#id13" id="id14"&gt;7&lt;/a&gt; application server.&lt;/p&gt;
&lt;p&gt;There is no prebuilt package for Firefox Sync on piCore so we need to download
the source and build the software locally.&lt;/p&gt;
&lt;div class="section" id="install-prerequisites"&gt;
&lt;h4&gt;Install Prerequisites&lt;/h4&gt;
&lt;p&gt;Start by installing prerequisites:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_e0da1175981a449fb61be94509a227dd-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; tce-load -wi python
&lt;a name="rest_code_e0da1175981a449fb61be94509a227dd-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; tce-load -wo python-dev
&lt;a name="rest_code_e0da1175981a449fb61be94509a227dd-3"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; tce-load -wo make
&lt;a name="rest_code_e0da1175981a449fb61be94509a227dd-4"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; tce-load -wo git
&lt;a name="rest_code_e0da1175981a449fb61be94509a227dd-5"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; tce-load -wo gcc
&lt;a name="rest_code_e0da1175981a449fb61be94509a227dd-6"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; tce-load -wo compiletc
&lt;a name="rest_code_e0da1175981a449fb61be94509a227dd-7"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; tce-load -i python-dev make git gcc compiletc
&lt;/pre&gt;&lt;p&gt;The &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;-wi&lt;/span&gt;&lt;/code&gt; switch adds the extension to the &lt;em&gt;OnBoot&lt;/em&gt; list which means
it is loaded on every boot.
The &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;-wo&lt;/span&gt;&lt;/code&gt; switch creates an &lt;em&gt;OnDemand&lt;/em&gt; item which we can load manually
with &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;tce-load&lt;/span&gt; &lt;span class="pre"&gt;-i&lt;/span&gt;&lt;/code&gt;.
We install only the base &lt;code class="docutils literal"&gt;python&lt;/code&gt; with the &lt;em&gt;OnBoot&lt;/em&gt; option because it
is the only package we need for running the app.
All other packages are only required for the build process.&lt;/p&gt;
&lt;p&gt;The installation additionally requires Python &lt;code class="docutils literal"&gt;virtualenv&lt;/code&gt;
for which we do not have a piCore package. But we can
&lt;a class="reference external" href="https://virtualenv.pypa.io/en/stable/installation/"&gt;install virtualenv from source&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/firefox-sync-server-on-a-raspberry-pi/#id15" id="id16"&gt;8&lt;/a&gt;:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_83bfcb7bfd67405c8dcc6395ba822a56-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; wget https://pypi.python.org/packages/8b/2c/c0d3e47709d0458816167002e1aa3d64d03bdeb2a9d57c5bd18448fd24cd/virtualenv-15.0.3.tar.gz
&lt;a name="rest_code_83bfcb7bfd67405c8dcc6395ba822a56-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; tar -xzf virtualenv-15.0.3.tar.gz
&lt;a name="rest_code_83bfcb7bfd67405c8dcc6395ba822a56-3"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; virtualenv-15.0.3
&lt;a name="rest_code_83bfcb7bfd67405c8dcc6395ba822a56-4"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo python setup.py install
&lt;a name="rest_code_83bfcb7bfd67405c8dcc6395ba822a56-5"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; ..
&lt;a name="rest_code_83bfcb7bfd67405c8dcc6395ba822a56-6"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; rm virtualenv-15.0.3.tar.gz
&lt;a name="rest_code_83bfcb7bfd67405c8dcc6395ba822a56-7"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; rm -r virtualenv-15.0.3
&lt;/pre&gt;&lt;p&gt;The download URL can be obtained from
&lt;a class="reference external" href="https://pypi.python.org/pypi/virtualenv"&gt;PyPi&lt;/a&gt;.&lt;/p&gt;
&lt;div class="admonition note"&gt;
&lt;p class="admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;Installing virtualenv in this way places installation files in their
default locations. They will be lost on shutdown.
This does not matter (much) as we only need &lt;code class="docutils literal"&gt;virtualenv&lt;/code&gt; for the
installation, not for running the application.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="create-an-ffsync-user"&gt;
&lt;h4&gt;Create an ffsync User&lt;/h4&gt;
&lt;p&gt;We want to run the sync service under a dedicated &lt;code class="docutils literal"&gt;ffsync&lt;/code&gt; user.
Create it like so:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_f0601efbd3b440b7ad46aaab22f49590-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo adduser ffsync -SH
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="section" id="installation-location"&gt;
&lt;h4&gt;Installation Location&lt;/h4&gt;
&lt;p&gt;Decide upon an installation location.&lt;/p&gt;
&lt;p&gt;The default installation procedure is to clone the &lt;a class="reference external" href="https://github.com/mozilla-services/syncserver"&gt;syncserver git repository&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/firefox-sync-server-on-a-raspberry-pi/#id17" id="id18"&gt;9&lt;/a&gt;
and build the application inside the repo directory.
Building the application entails creation of a virtual Python environment.
The &lt;em&gt;virtualenv&lt;/em&gt; is basically a copy of the system-wide
Python install plus additional packages installed in a local directory,
in this case the syncserver installation directory.&lt;/p&gt;
&lt;p&gt;This results in a large number of files and directories.
If we keep the installation inside the &lt;code class="docutils literal"&gt;/home&lt;/code&gt; directory,
we have the benefit of running it from memory but the disadvantage
that the backup and restore for the home directory takes quite long.&lt;/p&gt;
&lt;p&gt;If we install to a persistent partition, we lose the "running from
memory" advantage.&lt;/p&gt;
&lt;p&gt;The proper(?) solution is to create a TinyCore Linux extension for it.
This is described in detail in chapter 14 and 15 of the &lt;a class="reference external" href="http://tinycorelinux.net/corebook.pdf"&gt;Tiny Core book&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/firefox-sync-server-on-a-raspberry-pi/#id19" id="id20"&gt;10&lt;/a&gt;
[PDF].
The basic idea is to create the desired directory structure and files
in some temporary directory and then "pack" it with &lt;code class="docutils literal"&gt;squashfs&lt;/code&gt;.
TinyCore Linux will later mount/symlink it into the desired location.&lt;/p&gt;
&lt;p&gt;That is, if we want to have a file &lt;code class="docutils literal"&gt;/usr/share/myfile&lt;/code&gt;,
create &lt;code class="docutils literal"&gt;/tmp/myextension/usr/share/myfile&lt;/code&gt; and then use squashfs
to create &lt;code class="docutils literal"&gt;myextension.tcz&lt;/code&gt; from &lt;code class="docutils literal"&gt;/tmp/myextension&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The syncserver build process (more precisely: &lt;code class="docutils literal"&gt;virtualenv&lt;/code&gt;) will
generate some files with hardcoded paths so it is tricky to move the installation
to some place different than where it was created.
There is an option to make a &lt;a class="reference external" href="https://virtualenv.pypa.io/en/stable/userguide/#making-environments-relocatable"&gt;relocatable virtualenv&lt;/a&gt;
but it does not seem fully supported and we are not going to use it.&lt;/p&gt;
&lt;p&gt;The good thing is that the &lt;em&gt;virtualenv&lt;/em&gt; installation keeps all required files
under a single base directory. This makes it possible to install the application
at the desired location then pack it up and remove the original install.&lt;/p&gt;
&lt;p&gt;We will install under &lt;code class="docutils literal"&gt;/usr/local/syncserver&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;First create the required directory and clone the syncserver repo into it:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_4e78aab34eac4bc6b3329c22394eacc2-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; /usr/local
&lt;a name="rest_code_4e78aab34eac4bc6b3329c22394eacc2-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo mkdir syncserver
&lt;a name="rest_code_4e78aab34eac4bc6b3329c22394eacc2-3"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo chown syncserver tc:staff
&lt;a name="rest_code_4e78aab34eac4bc6b3329c22394eacc2-4"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; git clone --depth&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; https://github.com/mozilla-services/syncserver syncserver
&lt;a name="rest_code_4e78aab34eac4bc6b3329c22394eacc2-5"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; syncserver
&lt;a name="rest_code_4e78aab34eac4bc6b3329c22394eacc2-6"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; make build
&lt;/pre&gt;&lt;div class="admonition note"&gt;
&lt;p class="admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;Root permissions are only required to create a new directory in &lt;code class="docutils literal"&gt;/usr/local&lt;/code&gt;.
The installation itself works with normal permissions.&lt;/p&gt;
&lt;p&gt;Use &lt;code class="docutils literal"&gt;git clone&lt;/code&gt; with &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;--depth=1&lt;/span&gt;&lt;/code&gt; to retrieve as little history as possible,
but still keep it as a git repo.&lt;/p&gt;
&lt;p&gt;Delete the &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;syncserver/.git&lt;/span&gt;&lt;/code&gt; directory to save additional space.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Next, we need to install the Python database driver.
This is done using the Python package manager (&lt;code class="docutils literal"&gt;pip&lt;/code&gt;) that is part of the
&lt;em&gt;virtualenv&lt;/em&gt;.
So, from the syncserver directory:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_1d87889e6ffd4300ada165fb0d486b13-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; ./local/bin/pip install pysqlite
&lt;/pre&gt;&lt;p&gt;Install &lt;code class="docutils literal"&gt;pysqlite&lt;/code&gt; for Usage with SQLite, &lt;code class="docutils literal"&gt;PyMySQL&lt;/code&gt; for MySQL/MariaDB.&lt;/p&gt;
&lt;div class="admonition note"&gt;
&lt;p class="admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;The &lt;code class="docutils literal"&gt;make build&lt;/code&gt; and the installation of pysqlite/PyMySQL with &lt;code class="docutils literal"&gt;pip&lt;/code&gt;
will leave several files in &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;~/.cache/pip&lt;/span&gt;&lt;/code&gt;.
You may want to delete those so they will not be included in the backup.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;We should now have a complete installation at &lt;code class="docutils literal"&gt;/usr/share/syncserver&lt;/code&gt;.
Running &lt;code class="docutils literal"&gt;sudo make serve&lt;/code&gt; from that directory should start the server
with default settings on port 5000.&lt;/p&gt;
&lt;p&gt;Look at &lt;code class="docutils literal"&gt;./local/bin/gunicorn&lt;/code&gt;. On the top it should say:&lt;/p&gt;
&lt;pre class="literal-block"&gt;#!/usr/local/syncserver/local/bin/python2&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="config"&gt;
&lt;h4&gt;Config&lt;/h4&gt;
&lt;p&gt;The default setup assumes that the syncserver is started from inside the
installation directory with &lt;code class="docutils literal"&gt;make serve&lt;/code&gt; and that the configuration file
is kept there (it is referenced with &lt;code class="docutils literal"&gt;./syncserver.ini&lt;/code&gt;).
If we want to change config separately from the installation, we need to
copy it to another location:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_9de08a01cde74614b4f18edf38215327-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; cp syncserver.ini /etc
&lt;/pre&gt;&lt;p&gt;And also include it in &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;/opt/.filetool.lst&lt;/span&gt;&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="literal-block"&gt;etc/syncserver.ini&lt;/pre&gt;
&lt;p&gt;We will later use a custom command like this to run the service:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_b22b746c8ab84300a090339e11193e78-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; ./local/bin/gunicorn --paste /etc/syncserver.ini
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="section" id="create-the-extension"&gt;
&lt;h4&gt;Create the Extension&lt;/h4&gt;
&lt;p&gt;First off, install &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;squashfs-tools&lt;/span&gt;&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_a63eea15c2164ccb81c3865791e165a9-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; tce-load -wo squashfs-tools
&lt;/pre&gt;&lt;p&gt;To produce a TinyCore Linux extension, we need to transfer everything
to a temporary directory structure, pack it and move the
resulting &lt;code class="docutils literal"&gt;.tcz&lt;/code&gt; file to the &lt;code class="docutils literal"&gt;tce&lt;/code&gt; directory:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_0f831404a81f453eb9ce6d168b575258-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; /tmp
&lt;a name="rest_code_0f831404a81f453eb9ce6d168b575258-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; mkdir -p syncserver/usr/local
&lt;a name="rest_code_0f831404a81f453eb9ce6d168b575258-3"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo mv /usr/local/syncserver syncserver/usr/local
&lt;a name="rest_code_0f831404a81f453eb9ce6d168b575258-4"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo chown root:root -R syncserver/
&lt;a name="rest_code_0f831404a81f453eb9ce6d168b575258-5"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; mksquashfs syncserver syncserver.tcz
&lt;a name="rest_code_0f831404a81f453eb9ce6d168b575258-6"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; mv syncserver.tcz /etc/sysconfig/tcedir/optional
&lt;a name="rest_code_0f831404a81f453eb9ce6d168b575258-7"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo rm -r syncserver/
&lt;/pre&gt;&lt;div class="admonition note"&gt;
&lt;p class="admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;It is a good idea to place a backup of the &lt;code class="docutils literal"&gt;syncserver.tcz&lt;/code&gt;
file somewhere other then the &lt;code class="docutils literal"&gt;tce/optional&lt;/code&gt; directory.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;We can now start the application with:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_89df97756a6244d2a1b9eb076dc8eb57-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; tce-load -i syncserver
&lt;/pre&gt;&lt;p&gt;You should see the syncserver installation (as a collection of symlinks)
under &lt;code class="docutils literal"&gt;/usr/local/syncserver&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Note that &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;tce-run&lt;/span&gt;&lt;/code&gt; will not actually start the service. It will only
make the installed application available.
Running &lt;code class="docutils literal"&gt;sudo make serve&lt;/code&gt; from the installation directory should
now work as before.&lt;/p&gt;
&lt;p&gt;To have the application available on boot, add it to the &lt;code class="docutils literal"&gt;onboot.lst&lt;/code&gt;
(inside the &lt;code class="docutils literal"&gt;tce&lt;/code&gt; directory):&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_9683093bdb6a432a9ece64088d76ba0c-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; syncserver.tcz &amp;gt;&amp;gt; /etc/sysconfig/tcedir/onboot.lst
&lt;/pre&gt;&lt;!-- TODO: md5sum? --&gt;
&lt;/div&gt;
&lt;div class="section" id="start-and-stop-script"&gt;
&lt;h4&gt;Start and Stop Script&lt;/h4&gt;
&lt;p&gt;We need a custom script to start the service.
The start script is included in &lt;code class="docutils literal"&gt;bootlocal.sh&lt;/code&gt; to start the service
on boot and performs three tasks:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;change into the correct working directory (&lt;code class="docutils literal"&gt;/usr/local/syncserver&lt;/code&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;switch to the &lt;code class="docutils literal"&gt;ffsync&lt;/code&gt; user&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;start the syncserver with our configuration file at &lt;code class="docutils literal"&gt;/etc/syncserver.ini&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="code bash"&gt;&lt;a name="rest_code_829996ab7e22481397a0f49a16976188-1"&gt;&lt;/a&gt;&lt;span class="nv"&gt;BASEDIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/local/syncserver
&lt;a name="rest_code_829996ab7e22481397a0f49a16976188-2"&gt;&lt;/a&gt;&lt;span class="nv"&gt;GUNICORN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$BASEDIR&lt;/span&gt;/local/bin/gunicorn
&lt;a name="rest_code_829996ab7e22481397a0f49a16976188-3"&gt;&lt;/a&gt;&lt;span class="nv"&gt;CONFIG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/etc/syncserver.ini
&lt;a name="rest_code_829996ab7e22481397a0f49a16976188-4"&gt;&lt;/a&gt;&lt;span class="nv"&gt;COMMAND&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GUNICORN&lt;/span&gt;&lt;span class="s2"&gt; --paste &lt;/span&gt;&lt;span class="nv"&gt;$CONFIG&lt;/span&gt;&lt;span class="s2"&gt; --log-file /tmp/syncserver.log &amp;gt; /dev/null &amp;amp;"&lt;/span&gt;
&lt;a name="rest_code_829996ab7e22481397a0f49a16976188-5"&gt;&lt;/a&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="nv"&gt;$BASEDIR&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; su ffsync -c &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$COMMAND&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; -s /bin/sh&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;We still need to change the working directory before we execute the sync server.
If we do this inside the &lt;code class="docutils literal"&gt;bootlocal.sh&lt;/code&gt; script we might affect subsequent actions
in that script. &lt;a class="reference external" href="https://stackoverflow.com/questions/786376/how-do-i-run-a-program-with-a-different-working-directory-from-current-from-lin#786419"&gt;One way&lt;/a&gt;
to avoid this, invoke everything in a subshell.&lt;/p&gt;
&lt;p&gt;Also, since the &lt;code class="docutils literal"&gt;gunicorn&lt;/code&gt; command will not exit,
"disown" it afterwards with "&amp;amp;".&lt;/p&gt;
&lt;p&gt;To stop the service, use:&lt;/p&gt;
&lt;pre class="code bash"&gt;&lt;a name="rest_code_a71b7933cc0e42c1b25efaa0cf6b65f2-1"&gt;&lt;/a&gt;pkill syncserver
&lt;/pre&gt;&lt;p&gt;(not pretty)&lt;/p&gt;
&lt;!-- TODO: look into the ``pserve`` based solution here
http://www.raspberry-pi-geek.com/Archive/2015/11/The-new-Firefox-synchronizer --&gt;
&lt;/div&gt;
&lt;div class="section" id="configuration"&gt;
&lt;h4&gt;Configuration&lt;/h4&gt;
&lt;p&gt;Edit the config file to reflect your settings:&lt;/p&gt;
&lt;pre class="code ini"&gt;&lt;a name="rest_code_7ad709ee06aa4ea9b325c485d9b4f428-1"&gt;&lt;/a&gt;&lt;span class="k"&gt;[syncserver]&lt;/span&gt;
&lt;a name="rest_code_7ad709ee06aa4ea9b325c485d9b4f428-2"&gt;&lt;/a&gt;&lt;span class="na"&gt;public_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;http://box:5000/&lt;/span&gt;
&lt;a name="rest_code_7ad709ee06aa4ea9b325c485d9b4f428-3"&gt;&lt;/a&gt;&lt;span class="na"&gt;sqluri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;sqlite:////mnt/storage/syncserver/syncserver.db&lt;/span&gt;
&lt;a name="rest_code_7ad709ee06aa4ea9b325c485d9b4f428-4"&gt;&lt;/a&gt;&lt;span class="na"&gt;secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;xxx&lt;/span&gt;
&lt;a name="rest_code_7ad709ee06aa4ea9b325c485d9b4f428-5"&gt;&lt;/a&gt;&lt;span class="na"&gt;allow_new_users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;false  # set to true to create initial user&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;&lt;code class="docutils literal"&gt;public_url&lt;/code&gt; is set to what the client sees as the URL of the service.&lt;/p&gt;
&lt;p&gt;If you do not configure a &lt;code class="docutils literal"&gt;sqluri&lt;/code&gt;, the default applies - which is an
in-memory database (which is lost when the service is shut down).&lt;/p&gt;
&lt;p&gt;The &lt;code class="docutils literal"&gt;secret&lt;/code&gt; is used to generate authentication tokens.
It can be any random string. Generate one like this:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_be72a401e1144bebbc6635a2a5aefe09-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; head -c20 /dev/urandom &lt;span class="p"&gt;|&lt;/span&gt; sha1sum
&lt;/pre&gt;&lt;p&gt;&lt;code class="docutils literal"&gt;allow_new_users&lt;/code&gt; should be set to &lt;code class="docutils literal"&gt;true&lt;/code&gt; initially.
When the service is fully installed and all required Firefox accounts are
registered, it can be set to &lt;code class="docutils literal"&gt;false&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="install-a-database"&gt;
&lt;h3&gt;Install a Database&lt;/h3&gt;
&lt;p&gt;The syncserver works with SQLite, MySQL/MariaDB or PostgreSQL.
There are piCore packages for SQLite and MariaDB.&lt;/p&gt;
&lt;div class="section" id="sqlite"&gt;
&lt;h4&gt;SQLite&lt;/h4&gt;
&lt;p&gt;For SQLite, make sure the &lt;code class="docutils literal"&gt;sqlite3&lt;/code&gt; package is installed.
Then decide where the database file should live and update
&lt;code class="docutils literal"&gt;syncserver.ini&lt;/code&gt; accordingly:&lt;/p&gt;
&lt;pre class="code ini"&gt;&lt;a name="rest_code_a2e4e1e8b39249658b25e6f47a505c52-1"&gt;&lt;/a&gt;&lt;span class="k"&gt;[syncserver]&lt;/span&gt;
&lt;a name="rest_code_a2e4e1e8b39249658b25e6f47a505c52-2"&gt;&lt;/a&gt;&lt;span class="na"&gt;...&lt;/span&gt;
&lt;a name="rest_code_a2e4e1e8b39249658b25e6f47a505c52-3"&gt;&lt;/a&gt;&lt;span class="na"&gt;sqluri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;sqlite:////mnt/storage/syncserver/syncserver.db&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Remember to make the database directory accessible for the
&lt;code class="docutils literal"&gt;ffsync&lt;/code&gt; user.&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_3aad0e4cb8a14780a019f5aaa02de739-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo mkdir /mnt/storage/syncserver
&lt;a name="rest_code_3aad0e4cb8a14780a019f5aaa02de739-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo chown ffsync:staff /mnt/storage/syncserver
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="section" id="mariadb"&gt;
&lt;h4&gt;MariaDB&lt;/h4&gt;
&lt;p&gt;There is a (somewhat outdated?) &lt;a class="reference external" href="http://wiki.tinycorelinux.net/wiki:mysql_persistence_guide"&gt;MariaDB installation guide&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/firefox-sync-server-on-a-raspberry-pi/#id21" id="id22"&gt;11&lt;/a&gt; in the TinyCore Linux wiki.&lt;/p&gt;
&lt;p&gt;Start by installing MariaDB&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_8f4e3f468d5d498ea8a3e40132d15e77-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; tce-load -wi mariadb
&lt;a name="rest_code_8f4e3f468d5d498ea8a3e40132d15e77-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; tce-load -wi mariadb-client
&lt;/pre&gt;&lt;p&gt;Next, we make sure that the DB service starts on boot and stops on shutdown.
The package comes only with a partially complete configuration and some effort
is required to make it work.&lt;/p&gt;
&lt;p&gt;Start by creating a system account to run mariadb:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_ed7b01c589994f6c88c767b9c251df66-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo adduser -SH mysql
&lt;/pre&gt;&lt;p&gt;Next, add commands to &lt;a class="reference external" href="https://mariadb.com/kb/en/mariadb/starting-and-stopping-mariadb-automatically/"&gt;start and stop MariaDB&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/firefox-sync-server-on-a-raspberry-pi/#id23" id="id24"&gt;12&lt;/a&gt; to &lt;code class="docutils literal"&gt;bootlocal.sh&lt;/code&gt;
and &lt;code class="docutils literal"&gt;shutdown.sh&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="literal-block"&gt;# bootlocal.sh
/usr/local/share/mysql/mysql.server start

# shutdown.sh
/usr/local/share/mysql/mysql.server stop&lt;/pre&gt;
&lt;div class="admonition note"&gt;
&lt;p class="admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;Make sure that the database service is started before the syncserver,
and shut down after the syncserver.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Then create a minimal &lt;a class="reference external" href="https://mariadb.com/kb/en/mariadb/configuring-mariadb-with-mycnf/"&gt;configuration file for MariaDB&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/firefox-sync-server-on-a-raspberry-pi/#id25" id="id26"&gt;13&lt;/a&gt;
file at &lt;code class="docutils literal"&gt;/etc/my.cnf&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code ini"&gt;&lt;a name="rest_code_6a87e1783be244eeb258c8f1170d9094-1"&gt;&lt;/a&gt;&lt;span class="k"&gt;[client]&lt;/span&gt;
&lt;a name="rest_code_6a87e1783be244eeb258c8f1170d9094-2"&gt;&lt;/a&gt;&lt;span class="na"&gt;socket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;/tmp/mysql.sock&lt;/span&gt;
&lt;a name="rest_code_6a87e1783be244eeb258c8f1170d9094-3"&gt;&lt;/a&gt;
&lt;a name="rest_code_6a87e1783be244eeb258c8f1170d9094-4"&gt;&lt;/a&gt;&lt;span class="k"&gt;[mysqld]&lt;/span&gt;
&lt;a name="rest_code_6a87e1783be244eeb258c8f1170d9094-5"&gt;&lt;/a&gt;&lt;span class="na"&gt;socket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;/tmp/mysql.sock&lt;/span&gt;
&lt;a name="rest_code_6a87e1783be244eeb258c8f1170d9094-6"&gt;&lt;/a&gt;&lt;span class="na"&gt;basedir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;/usr/local&lt;/span&gt;
&lt;a name="rest_code_6a87e1783be244eeb258c8f1170d9094-7"&gt;&lt;/a&gt;&lt;span class="na"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;mysql&lt;/span&gt;
&lt;a name="rest_code_6a87e1783be244eeb258c8f1170d9094-8"&gt;&lt;/a&gt;&lt;span class="na"&gt;datadir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;/usr/local/mysql/data&lt;/span&gt;
&lt;a name="rest_code_6a87e1783be244eeb258c8f1170d9094-9"&gt;&lt;/a&gt;&lt;span class="na"&gt;tmpdir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;/tmp&lt;/span&gt;
&lt;a name="rest_code_6a87e1783be244eeb258c8f1170d9094-10"&gt;&lt;/a&gt;&lt;span class="na"&gt;log_error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;/tmp/mysql.error.log&lt;/span&gt;
&lt;a name="rest_code_6a87e1783be244eeb258c8f1170d9094-11"&gt;&lt;/a&gt;&lt;span class="na"&gt;pid_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;/tmp/mysql.pid&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;It is important to set the &lt;code class="docutils literal"&gt;socket&lt;/code&gt; option in the &lt;code class="docutils literal"&gt;[server]&lt;/code&gt;
&lt;em&gt;and&lt;/em&gt; &lt;code class="docutils literal"&gt;[client]&lt;/code&gt; sections. Otherwise tools like &lt;code class="docutils literal"&gt;mysqladmin&lt;/code&gt; will not
see the socket option and will fail to connect.&lt;/p&gt;
&lt;p&gt;Make sure that the &lt;code class="docutils literal"&gt;datadir&lt;/code&gt; is accessible for the &lt;code class="docutils literal"&gt;mysql&lt;/code&gt; user:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_6c1042eb5f6342a8aa3b84773c21602d-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo chown -R mysql:nogroup /usr/local/mysql/data
&lt;/pre&gt;&lt;!-- TODO Also pidfile, socket and log file(s). --&gt;
&lt;p&gt;Manually starting and stopping &lt;code class="docutils literal"&gt;mysqld&lt;/code&gt; should now work:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_790d7b19104f442787faecd1b69a2ca3-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo /usr/local/share/mysql/mysql.server start
&lt;a name="rest_code_790d7b19104f442787faecd1b69a2ca3-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;Starting MySQL. SUCCESS!&lt;/span&gt;
&lt;a name="rest_code_790d7b19104f442787faecd1b69a2ca3-3"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo /usr/local/share/mysql/mysql.server stop
&lt;a name="rest_code_790d7b19104f442787faecd1b69a2ca3-4"&gt;&lt;/a&gt;&lt;span class="go"&gt;Shutting down MySQL.. SUCCESS!&lt;/span&gt;
&lt;/pre&gt;&lt;div class="section" id="persistence"&gt;
&lt;h5&gt;Persistence&lt;/h5&gt;
&lt;p&gt;For now, the config file and data directory only exist in memory.
To persist the config file between reboots, add it to the backup list
in &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;/opt/.filetool.lst&lt;/span&gt;&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="literal-block"&gt;etc/my.cnf&lt;/pre&gt;
&lt;p&gt;The data directory is where the database files are stored.
By default, this is &lt;code class="docutils literal"&gt;/usr/local/mysql/data&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If this would be kept in memory and backed up/restored when the system
restarts, we have a risk of losing data when the system shuts down
unexpectedly and the backup script does not run
(e.g. when power is unplugged).&lt;/p&gt;
&lt;p&gt;To avoid this, keep the database on a separate partition
and create a symlink in the original location which points
to the new location.
Stop the &lt;code class="docutils literal"&gt;mysqld&lt;/code&gt; service before moving the data directory.&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_62e2f20452d746b884dab1251ca2b4dd-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo /usr/local/share/mysql/mysql.server stop
&lt;a name="rest_code_62e2f20452d746b884dab1251ca2b4dd-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo mkdir /mnt/storage/mysql
&lt;a name="rest_code_62e2f20452d746b884dab1251ca2b4dd-3"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo chown mysql:nogroup /mnt/storage/mysql
&lt;a name="rest_code_62e2f20452d746b884dab1251ca2b4dd-4"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo mv /usr/local/mysql/data /mnt/storage/mysql
&lt;a name="rest_code_62e2f20452d746b884dab1251ca2b4dd-5"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo ln -s /mnt/storage/mysql/data /usr/local/mysql
&lt;a name="rest_code_62e2f20452d746b884dab1251ca2b4dd-6"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo /usr/local/share/mysql/mysql.server start
&lt;/pre&gt;&lt;p&gt;We need to repeat parts of this on every reboot. So add the following
to &lt;code class="docutils literal"&gt;bootlocal.sh&lt;/code&gt; (&lt;em&gt;before&lt;/em&gt; starting mysqld):&lt;/p&gt;
&lt;pre class="code bash"&gt;&lt;a name="rest_code_c045ea19c47747e985b9efe8b02cbf10-1"&gt;&lt;/a&gt;&lt;span class="c1"&gt;# replace the standard datadir with persisted data&lt;/span&gt;
&lt;a name="rest_code_c045ea19c47747e985b9efe8b02cbf10-2"&gt;&lt;/a&gt;&lt;span class="nv"&gt;MYSQL_DATADIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/local/mysql/data
&lt;a name="rest_code_c045ea19c47747e985b9efe8b02cbf10-3"&gt;&lt;/a&gt;rm -rf &lt;span class="nv"&gt;$MYSQL_DATADIR&lt;/span&gt;
&lt;a name="rest_code_c045ea19c47747e985b9efe8b02cbf10-4"&gt;&lt;/a&gt;ln -s /mnt/storage/mysql/data &lt;span class="nv"&gt;$MYSQL_DATADIR&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="section" id="setup-a-database-user"&gt;
&lt;h5&gt;Setup a Database User&lt;/h5&gt;
&lt;p&gt;First, set a password for mysql admin&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_3482bd7b13a642518bb6d3dce832575d-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; mysqladmin -u root password &amp;lt;secret&amp;gt;
&lt;/pre&gt;&lt;p&gt;Login to mysql console to create the user and database for &lt;code class="docutils literal"&gt;ffsync&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_73c9f3a6ee274048b386cada0c3787ff-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; mysql -u root -p
&lt;a name="rest_code_73c9f3a6ee274048b386cada0c3787ff-2"&gt;&lt;/a&gt;&lt;span class="gp gp-VirtualEnv"&gt;(enter password when prompted)&lt;/span&gt;
&lt;a name="rest_code_73c9f3a6ee274048b386cada0c3787ff-3"&gt;&lt;/a&gt;&lt;span class="go"&gt;mysql&amp;gt; CREATE USER 'ffsync'@'localhost' IDENTIFIED BY '&amp;lt;secret&amp;gt;';&lt;/span&gt;
&lt;a name="rest_code_73c9f3a6ee274048b386cada0c3787ff-4"&gt;&lt;/a&gt;&lt;span class="go"&gt;mysql&amp;gt; CREATE DATABASE ffsync;&lt;/span&gt;
&lt;a name="rest_code_73c9f3a6ee274048b386cada0c3787ff-5"&gt;&lt;/a&gt;&lt;span class="go"&gt;mysql&amp;gt; GRANT ALL ON ffsync.* TO 'ffsync'@'localhost';&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Finally, configure Firefox Sync Server to use the MariaDB database:&lt;/p&gt;
&lt;pre class="code ini"&gt;&lt;a name="rest_code_99ecc6c7bf7e4564a1228117aa850b7f-1"&gt;&lt;/a&gt;&lt;span class="k"&gt;[syncserver]&lt;/span&gt;
&lt;a name="rest_code_99ecc6c7bf7e4564a1228117aa850b7f-2"&gt;&lt;/a&gt;&lt;span class="na"&gt;...&lt;/span&gt;
&lt;a name="rest_code_99ecc6c7bf7e4564a1228117aa850b7f-3"&gt;&lt;/a&gt;&lt;span class="na"&gt;sqluri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;pymysql://ffsync:&amp;lt;secret&amp;gt;@localhost/ffsync&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="configure-firefox"&gt;
&lt;h3&gt;Configure Firefox&lt;/h3&gt;
&lt;p&gt;Go to &lt;code class="docutils literal"&gt;about:config&lt;/code&gt; and set:&lt;/p&gt;
&lt;pre class="literal-block"&gt;identity.sync.tokenserver.uri = http://box:5000/token/1.0/sync/1.5&lt;/pre&gt;
&lt;p&gt;(the default is &lt;a class="reference external" href="https://token.services.mozilla.com/1.0/sync/1.5"&gt;https://token.services.mozilla.com/1.0/sync/1.5&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;You will still need a firefox account to use the sync feature.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="troubleshooting"&gt;
&lt;h3&gt;Troubleshooting&lt;/h3&gt;
&lt;p&gt;syncserver complains that &lt;code class="docutils literal"&gt;public_url&lt;/code&gt; does not match &lt;code class="docutils literal"&gt;application url&lt;/code&gt;.
Public URL is set to "&lt;a class="reference external" href="http://hostname:5000/"&gt;http://hostname:5000/&lt;/a&gt;", error says that app url is
"&lt;a class="reference external" href="http://hostname/"&gt;http://hostname/&lt;/a&gt;".
Change &lt;code class="docutils literal"&gt;public_url&lt;/code&gt; in config file to hostname w/o port,
get the same error but the other way round.
Set &lt;code class="docutils literal"&gt;force_wsgi_environ = true&lt;/code&gt; fixed this.&lt;/p&gt;
&lt;p&gt;With SQLite - errors when clients try to sync:&lt;/p&gt;
&lt;pre class="literal-block"&gt;(OperationalError) cannot start a transaction within a transaction&lt;/pre&gt;
&lt;p&gt;Tried to limit gunicorn to a single worker thread - no help.
Installed MariaDB to work around this.&lt;/p&gt;
&lt;div class="admonition note"&gt;
&lt;p class="admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;Type &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;about:sync-log&lt;/span&gt;&lt;/code&gt; into Firefox address bar to see ...sync logs.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Some additional links:
- &lt;a class="reference external" href="http://blog.sysbite.org/run-a-firefox-sync-server-on-the-raspberry-pi/"&gt;http://blog.sysbite.org/run-a-firefox-sync-server-on-the-raspberry-pi/&lt;/a&gt;
- &lt;a class="reference external" href="http://www.raspberry-pi-geek.com/Archive/2015/11/The-new-Firefox-synchronizer"&gt;http://www.raspberry-pi-geek.com/Archive/2015/11/The-new-Firefox-synchronizer&lt;/a&gt;
- &lt;a class="reference external" href="https://wiki.archlinux.org/index.php/Mozilla_Firefox_Sync_Server"&gt;https://wiki.archlinux.org/index.php/Mozilla_Firefox_Sync_Server&lt;/a&gt;&lt;/p&gt;
&lt;hr class="docutils"&gt;
&lt;dl class="footnote brackets"&gt;
&lt;dt class="label" id="id1"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/firefox-sync-server-on-a-raspberry-pi/#id2"&gt;1&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="https://docs.services.mozilla.com/howtos/run-sync-1.5.html"&gt;https://docs.services.mozilla.com/howtos/run-sync-1.5.html&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id3"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/firefox-sync-server-on-a-raspberry-pi/#id4"&gt;2&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="http://tinycorelinux.net/8.x/armv6/releases/RPi/README"&gt;http://tinycorelinux.net/8.x/armv6/releases/RPi/README&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id5"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/firefox-sync-server-on-a-raspberry-pi/#id6"&gt;3&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="http://tinycorelinux.net/"&gt;http://tinycorelinux.net/&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id7"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/firefox-sync-server-on-a-raspberry-pi/#id8"&gt;4&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="http://www.tldp.org/HOWTO/Flash-Memory-HOWTO/ext2.html"&gt;http://www.tldp.org/HOWTO/Flash-Memory-HOWTO/ext2.html&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id9"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/firefox-sync-server-on-a-raspberry-pi/#id10"&gt;5&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="https://virtualenv.pypa.io/en/stable/"&gt;https://virtualenv.pypa.io/en/stable/&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id11"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/firefox-sync-server-on-a-raspberry-pi/#id12"&gt;6&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="https://trypyramid.com/"&gt;https://trypyramid.com/&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id13"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/firefox-sync-server-on-a-raspberry-pi/#id14"&gt;7&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="http://gunicorn.org/"&gt;http://gunicorn.org/&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id15"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/firefox-sync-server-on-a-raspberry-pi/#id16"&gt;8&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="https://virtualenv.pypa.io/en/stable/installation/"&gt;https://virtualenv.pypa.io/en/stable/installation/&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id17"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/firefox-sync-server-on-a-raspberry-pi/#id18"&gt;9&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="https://github.com/mozilla-services/syncserver"&gt;https://github.com/mozilla-services/syncserver&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id19"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/firefox-sync-server-on-a-raspberry-pi/#id20"&gt;10&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="http://tinycorelinux.net/corebook.pdf"&gt;http://tinycorelinux.net/corebook.pdf&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id21"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/firefox-sync-server-on-a-raspberry-pi/#id22"&gt;11&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="http://wiki.tinycorelinux.net/wiki:mysql_persistence_guide"&gt;http://wiki.tinycorelinux.net/wiki:mysql_persistence_guide&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id23"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/firefox-sync-server-on-a-raspberry-pi/#id24"&gt;12&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="https://mariadb.com/kb/en/mariadb/starting-and-stopping-mariadb-automatically/"&gt;https://mariadb.com/kb/en/mariadb/starting-and-stopping-mariadb-automatically/&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id25"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/firefox-sync-server-on-a-raspberry-pi/#id26"&gt;13&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="https://mariadb.com/kb/en/mariadb/configuring-mariadb-with-mycnf/"&gt;https://mariadb.com/kb/en/mariadb/configuring-mariadb-with-mycnf/&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/div&gt;
&lt;/div&gt;</description><category>homeserver</category><category>linux</category><category>raspi</category><category>tinycore</category><guid>https://akeil.de/posts/firefox-sync-server-on-a-raspberry-pi/</guid><pubDate>Wed, 14 Sep 2016 15:50:27 GMT</pubDate></item><item><title>podfetch 0.4.3</title><link>https://akeil.de/posts/podfetch-0-4-3/</link><dc:creator>Alexander Keil</dc:creator><description>&lt;div class="section" id="podfetch-0-4-3"&gt;
&lt;h2&gt;podfetch 0.4.3&lt;/h2&gt;
&lt;p&gt;Changes in &lt;strong&gt;podfetch 0.4.3&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;The &lt;code class="docutils literal"&gt;ls&lt;/code&gt; and &lt;code class="docutils literal"&gt;update&lt;/code&gt; commands accept shell wildcards
for subscription names:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_f2e0b16d60ad4274b119ffffcde03775-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; podfetch ls f?o ba*
&lt;/pre&gt;&lt;p&gt;Lists subscriptions &lt;em&gt;foo&lt;/em&gt;, &lt;em&gt;bar&lt;/em&gt; and &lt;em&gt;baz&lt;/em&gt;
but not &lt;em&gt;something-else&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;If the subscriptions directory contains files that are not podfetch
subscriptions, a &lt;strong&gt;new config option&lt;/strong&gt; &lt;code class="docutils literal"&gt;ignore&lt;/code&gt; can be used to tell podfetch
to ignore these:&lt;/p&gt;
&lt;pre class="code ini"&gt;&lt;a name="rest_code_206a7278f45a4f2aa6f60a0408c63f49-1"&gt;&lt;/a&gt;&lt;span class="k"&gt;[podfetch]&lt;/span&gt;
&lt;a name="rest_code_206a7278f45a4f2aa6f60a0408c63f49-2"&gt;&lt;/a&gt;&lt;span class="na"&gt;ignore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;.* *.bak&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;The option expects a whitespace separated list of patterns
that should be ignored.&lt;/p&gt;
&lt;p&gt;See the &lt;a class="reference external" href="https://akeil.de/code/podfetch/"&gt;podfetch&lt;/a&gt; main page.&lt;/p&gt;
&lt;/div&gt;</description><category>code</category><category>podfetch</category><category>python</category><guid>https://akeil.de/posts/podfetch-0-4-3/</guid><pubDate>Sat, 28 Mar 2015 23:00:00 GMT</pubDate></item><item><title>Coordinate Scripts With systemd</title><link>https://akeil.de/posts/coordinate-scripts-with-systemd/</link><dc:creator>Alexander Keil</dc:creator><description>&lt;div class="section" id="coordinate-scripts-with-systemd"&gt;
&lt;h2&gt;Coordinate Scripts With systemd&lt;/h2&gt;
&lt;dl class="field-list simple"&gt;
&lt;dt&gt;author&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;akeil&lt;/p&gt;
&lt;/dd&gt;
&lt;dt&gt;date&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;2013-12-14&lt;/p&gt;
&lt;/dd&gt;
&lt;dt&gt;version&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;1&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;Use systemd to execute scripts,
controlling the order of execution.&lt;/p&gt;
&lt;dl class="simple"&gt;
&lt;dt&gt;Assuming we have:&lt;/dt&gt;
&lt;dd&gt;&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;a single preparation unit - &lt;code class="docutils literal"&gt;prep.service&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;multiple "task" units - &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;task-N.service&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;a single cleanup script - &lt;code class="docutils literal"&gt;done.service&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;We want to run the &lt;em&gt;prep unit&lt;/em&gt; once,
then run all our &lt;em&gt;task units&lt;/em&gt;
and finally the &lt;em&gt;done unit&lt;/em&gt; once.&lt;/p&gt;
&lt;p&gt;Defining dependencies between tasks is relatively simple when each task
is represented by a &lt;a class="reference external" href="http://www.freedesktop.org/software/systemd/man/systemd.unit.html"&gt;systemd unit&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/coordinate-scripts-with-systemd/#id1" id="id2"&gt;1&lt;/a&gt;.
Dependencies are defined using &lt;code class="docutils literal"&gt;Requires=&lt;/code&gt; or &lt;code class="docutils literal"&gt;Wants=&lt;/code&gt;
and order of execution is defined with &lt;code class="docutils literal"&gt;After=&lt;/code&gt; and &lt;code class="docutils literal"&gt;Before=&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The only thing left is to make sure that the "preparation"
and "finish" steps are executed only once, even if multiple tasks are run
(and not once for each task).&lt;/p&gt;
&lt;p&gt;To achieve this a &lt;code class="docutils literal"&gt;.target&lt;/code&gt; is created
and all tasks are associated to that target.&lt;/p&gt;
&lt;!-- TEASER_END --&gt;
&lt;div class="admonition note"&gt;
&lt;p class="admonition-title"&gt;Note&lt;/p&gt;
&lt;dl class="simple"&gt;
&lt;dt&gt;Steps:&lt;/dt&gt;
&lt;dd&gt;&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;Create &lt;code class="docutils literal"&gt;.service&lt;/code&gt; files for the commands that must be executed &lt;em&gt;before&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create &lt;code class="docutils literal"&gt;.service&lt;/code&gt; for commands that should be executed &lt;em&gt;after&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create as single &lt;code class="docutils literal"&gt;.target&lt;/code&gt; for all tasks&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a &lt;code class="docutils literal"&gt;.service&lt;/code&gt; for each task, make it &lt;code class="docutils literal"&gt;WantedBy=&lt;/code&gt; the target.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/div&gt;
&lt;div class="section" id="target-unit"&gt;
&lt;h3&gt;Target Unit&lt;/h3&gt;
&lt;p&gt;A &lt;a class="reference external" href="http://www.freedesktop.org/software/systemd/man/systemd.target.html"&gt;systemd target&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/coordinate-scripts-with-systemd/#id3" id="id4"&gt;2&lt;/a&gt; is used to group several units together.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;target&lt;/em&gt; file goes into &lt;code class="docutils literal"&gt;/etc/systemd/system&lt;/code&gt; and looks like this:&lt;/p&gt;
&lt;pre class="code ini"&gt;&lt;a name="rest_code_e7c75c92097c41c0a61f5a55125bd5c8-1"&gt;&lt;/a&gt;&lt;span class="c1"&gt;# tasks.target ---------------------------------------&lt;/span&gt;
&lt;a name="rest_code_e7c75c92097c41c0a61f5a55125bd5c8-2"&gt;&lt;/a&gt;&lt;span class="k"&gt;[Unit]&lt;/span&gt;
&lt;a name="rest_code_e7c75c92097c41c0a61f5a55125bd5c8-3"&gt;&lt;/a&gt;&lt;span class="na"&gt;Description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;Tasks Target&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;The target can be started manually with:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_91cdad81cfc045cd809226b8df2be069-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;#&lt;/span&gt; systemctl start tasks.target
&lt;/pre&gt;&lt;p&gt;The &lt;em&gt;task units&lt;/em&gt; are associated with the target using &lt;code class="docutils literal"&gt;WantedBy=&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This means, if we &lt;em&gt;enable&lt;/em&gt; one or more &lt;em&gt;task units&lt;/em&gt; with&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_50eb4ceb35f145f292203870b6683152-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;#&lt;/span&gt; systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; task-1.service task-2.service
&lt;/pre&gt;&lt;p&gt;... starting the &lt;code class="docutils literal"&gt;tasks.target&lt;/code&gt; will start all of the associated
units.&lt;/p&gt;
&lt;p&gt;This allows us to start all tasks with a single invocation
which in turn means that we execute any number of tasks
and the &lt;em&gt;prep&lt;/em&gt; and &lt;em&gt;done&lt;/em&gt; units only once.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="task-units"&gt;
&lt;h3&gt;Task Units&lt;/h3&gt;
&lt;p&gt;Each &lt;em&gt;task unit&lt;/em&gt; specifies the &lt;code class="docutils literal"&gt;prep.service&lt;/code&gt; as a
precondition using &lt;code class="docutils literal"&gt;Requires=&lt;/code&gt; and &lt;code class="docutils literal"&gt;After=&lt;/code&gt;.
It also specifies the &lt;code class="docutils literal"&gt;done.service&lt;/code&gt; using &lt;code class="docutils literal"&gt;Wants=&lt;/code&gt;
and &lt;code class="docutils literal"&gt;Before=&lt;/code&gt; to have it run after the tasks.&lt;/p&gt;
&lt;pre class="code ini"&gt;&lt;a name="rest_code_7a31e88d12b1475a9a15985e4752a076-1"&gt;&lt;/a&gt;&lt;span class="c1"&gt;# task.service ---------------------------------------&lt;/span&gt;
&lt;a name="rest_code_7a31e88d12b1475a9a15985e4752a076-2"&gt;&lt;/a&gt;&lt;span class="k"&gt;[Unit]&lt;/span&gt;
&lt;a name="rest_code_7a31e88d12b1475a9a15985e4752a076-3"&gt;&lt;/a&gt;&lt;span class="na"&gt;Description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;A task unit&lt;/span&gt;
&lt;a name="rest_code_7a31e88d12b1475a9a15985e4752a076-4"&gt;&lt;/a&gt;&lt;span class="na"&gt;Requires&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;prep.service&lt;/span&gt;
&lt;a name="rest_code_7a31e88d12b1475a9a15985e4752a076-5"&gt;&lt;/a&gt;&lt;span class="na"&gt;After&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;prep.service&lt;/span&gt;
&lt;a name="rest_code_7a31e88d12b1475a9a15985e4752a076-6"&gt;&lt;/a&gt;&lt;span class="na"&gt;Wants&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;done.service&lt;/span&gt;
&lt;a name="rest_code_7a31e88d12b1475a9a15985e4752a076-7"&gt;&lt;/a&gt;&lt;span class="na"&gt;Before&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;done.service&lt;/span&gt;
&lt;a name="rest_code_7a31e88d12b1475a9a15985e4752a076-8"&gt;&lt;/a&gt;
&lt;a name="rest_code_7a31e88d12b1475a9a15985e4752a076-9"&gt;&lt;/a&gt;&lt;span class="k"&gt;[Service]&lt;/span&gt;
&lt;a name="rest_code_7a31e88d12b1475a9a15985e4752a076-10"&gt;&lt;/a&gt;&lt;span class="na"&gt;User&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;someuser&lt;/span&gt;
&lt;a name="rest_code_7a31e88d12b1475a9a15985e4752a076-11"&gt;&lt;/a&gt;&lt;span class="na"&gt;Group&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;somegroup&lt;/span&gt;
&lt;a name="rest_code_7a31e88d12b1475a9a15985e4752a076-12"&gt;&lt;/a&gt;&lt;span class="na"&gt;Type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;oneshot&lt;/span&gt;
&lt;a name="rest_code_7a31e88d12b1475a9a15985e4752a076-13"&gt;&lt;/a&gt;&lt;span class="na"&gt;ExecStart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;/usr/local/bin/script.sh&lt;/span&gt;
&lt;a name="rest_code_7a31e88d12b1475a9a15985e4752a076-14"&gt;&lt;/a&gt;
&lt;a name="rest_code_7a31e88d12b1475a9a15985e4752a076-15"&gt;&lt;/a&gt;&lt;span class="k"&gt;[Install]&lt;/span&gt;
&lt;a name="rest_code_7a31e88d12b1475a9a15985e4752a076-16"&gt;&lt;/a&gt;&lt;span class="na"&gt;WantedBy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;tasks.target&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;The &lt;code class="docutils literal"&gt;Requires=prep.service&lt;/code&gt; directive means that the task is not started
if the &lt;code class="docutils literal"&gt;prep.service&lt;/code&gt; failed.
Using &lt;code class="docutils literal"&gt;Wants=prep.service&lt;/code&gt; would try to start the &lt;em&gt;prep unit&lt;/em&gt; first
but continue with the tasks regardless of whether preparation was
successful or not.&lt;/p&gt;
&lt;p&gt;The &lt;code class="docutils literal"&gt;User=&lt;/code&gt; and &lt;code class="docutils literal"&gt;Group=&lt;/code&gt; properties can be used to run the command
under a different user than root.&lt;/p&gt;
&lt;p&gt;Not all tasks have to specify the same dependencies.
It is also possible that tasks depend on each other.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="prep-and-done-units"&gt;
&lt;h3&gt;Prep and Done Units&lt;/h3&gt;
&lt;p&gt;The &lt;code class="docutils literal"&gt;prep.service&lt;/code&gt; and the &lt;code class="docutils literal"&gt;done.service&lt;/code&gt; are plain units
and do not define any dependencies.&lt;/p&gt;
&lt;p&gt;the &lt;em&gt;prep unit&lt;/em&gt; should be configured with &lt;code class="docutils literal"&gt;Type=oneshot&lt;/code&gt;
to have it complete before the first task can be started.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="service-type"&gt;
&lt;h3&gt;Service Type&lt;/h3&gt;
&lt;p&gt;By default, &lt;code class="docutils literal"&gt;Before=&lt;/code&gt; and &lt;code class="docutils literal"&gt;After=&lt;/code&gt; only control the order in which
services are &lt;em&gt;started&lt;/em&gt;.
To make sure that the &lt;em&gt;prep unit&lt;/em&gt; completes before the tasks are started,
use &lt;code class="docutils literal"&gt;Type=oneshot&lt;/code&gt; in the &lt;code class="docutils literal"&gt;[Service]&lt;/code&gt; definition.&lt;/p&gt;
&lt;p&gt;From the &lt;a class="reference external" href="http://www.freedesktop.org/software/systemd/man/systemd.service.html"&gt;systemd service&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/coordinate-scripts-with-systemd/#id5" id="id6"&gt;3&lt;/a&gt; documentation:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Behavior of oneshot is similar to simple;
however, it is expected that the process has to exit
before systemd starts follow-up units.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The same goes for the &lt;em&gt;task units&lt;/em&gt; if the &lt;em&gt;done unit&lt;/em&gt; should only
be executed after the tasks are complete.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="locations"&gt;
&lt;h3&gt;Locations&lt;/h3&gt;
&lt;dl class="simple"&gt;
&lt;dt&gt;Custom unit files go into:&lt;/dt&gt;
&lt;dd&gt;&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal"&gt;/etc/systemd/system&lt;/code&gt; for system-wide scope&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;~/.config/systemd/user&lt;/span&gt;&lt;/code&gt; for user-specific scope&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;hr class="docutils"&gt;
&lt;dl class="footnote brackets"&gt;
&lt;dt class="label" id="id1"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/coordinate-scripts-with-systemd/#id2"&gt;1&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="http://www.freedesktop.org/software/systemd/man/systemd.unit.html"&gt;http://www.freedesktop.org/software/systemd/man/systemd.unit.html&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id3"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/coordinate-scripts-with-systemd/#id4"&gt;2&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="http://www.freedesktop.org/software/systemd/man/systemd.target.html"&gt;http://www.freedesktop.org/software/systemd/man/systemd.target.html&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id5"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/coordinate-scripts-with-systemd/#id6"&gt;3&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="http://www.freedesktop.org/software/systemd/man/systemd.service.html"&gt;http://www.freedesktop.org/software/systemd/man/systemd.service.html&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/div&gt;
&lt;/div&gt;</description><category>linux</category><category>systemd</category><guid>https://akeil.de/posts/coordinate-scripts-with-systemd/</guid><pubDate>Sun, 14 Dec 2014 13:43:38 GMT</pubDate></item><item><title>Syncthing on Arch Linux</title><link>https://akeil.de/posts/syncthing-on-arch-linux/</link><dc:creator>Alexander Keil</dc:creator><description>&lt;div class="section" id="syncthing-on-arch-linux"&gt;
&lt;h2&gt;Syncthing on Arch Linux&lt;/h2&gt;
&lt;dl class="field-list simple"&gt;
&lt;dt&gt;author&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;akeil&lt;/p&gt;
&lt;/dd&gt;
&lt;dt&gt;date&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;2014-12-10&lt;/p&gt;
&lt;/dd&gt;
&lt;dt&gt;version&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;1&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;&lt;a class="reference external" href="http://syncthing.net/"&gt;Syncthing&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/syncthing-on-arch-linux/#id1" id="id2"&gt;1&lt;/a&gt; is an open source tool which keeps files
on different machines in sync.&lt;/p&gt;
&lt;!-- TEASER_END --&gt;
&lt;div class="section" id="installation"&gt;
&lt;h3&gt;Installation&lt;/h3&gt;
&lt;p&gt;For Arch Linux Syncthing is available in the &lt;a class="reference external" href="https://www.archlinux.org/packages/community/x86_64/syncthing/"&gt;community repository&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/syncthing-on-arch-linux/#id5" id="id6"&gt;3&lt;/a&gt;
and can be installed easily:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_c585698c890040e8a81f3d8b4a80a6e7-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;#&lt;/span&gt; pacman -S syncthing
&lt;/pre&gt;&lt;p&gt;The Syncthing package installs a &lt;em&gt;.service&lt;/em&gt; file at
&lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;/usr/lib/systemd/system/syncthing@.service&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;pre class="code ini"&gt;&lt;a name="rest_code_ed53a52e93b745cba24ce30ee89c3b2b-1"&gt;&lt;/a&gt;&lt;span class="k"&gt;[Unit]&lt;/span&gt;
&lt;a name="rest_code_ed53a52e93b745cba24ce30ee89c3b2b-2"&gt;&lt;/a&gt;&lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Syncthing service for %i&lt;/span&gt;
&lt;a name="rest_code_ed53a52e93b745cba24ce30ee89c3b2b-3"&gt;&lt;/a&gt;&lt;span class="na"&gt;After&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;network.target&lt;/span&gt;
&lt;a name="rest_code_ed53a52e93b745cba24ce30ee89c3b2b-4"&gt;&lt;/a&gt;
&lt;a name="rest_code_ed53a52e93b745cba24ce30ee89c3b2b-5"&gt;&lt;/a&gt;&lt;span class="k"&gt;[Service]&lt;/span&gt;
&lt;a name="rest_code_ed53a52e93b745cba24ce30ee89c3b2b-6"&gt;&lt;/a&gt;&lt;span class="na"&gt;User&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;%i&lt;/span&gt;
&lt;a name="rest_code_ed53a52e93b745cba24ce30ee89c3b2b-7"&gt;&lt;/a&gt;&lt;span class="na"&gt;Environment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;STNORESTART=yes&lt;/span&gt;
&lt;a name="rest_code_ed53a52e93b745cba24ce30ee89c3b2b-8"&gt;&lt;/a&gt;&lt;span class="na"&gt;ExecStart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/usr/bin/syncthing&lt;/span&gt;
&lt;a name="rest_code_ed53a52e93b745cba24ce30ee89c3b2b-9"&gt;&lt;/a&gt;&lt;span class="na"&gt;Restart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;always&lt;/span&gt;
&lt;a name="rest_code_ed53a52e93b745cba24ce30ee89c3b2b-10"&gt;&lt;/a&gt;
&lt;a name="rest_code_ed53a52e93b745cba24ce30ee89c3b2b-11"&gt;&lt;/a&gt;&lt;span class="k"&gt;[Install]&lt;/span&gt;
&lt;a name="rest_code_ed53a52e93b745cba24ce30ee89c3b2b-12"&gt;&lt;/a&gt;&lt;span class="na"&gt;WantedBy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;multi-user.target&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;When enabling or starting the service,
supply the user under which syncthing will run:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_6a0eea4bd3314fc9a616fa6f7e7dd5d3-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;#&lt;/span&gt; systemctl start syncthing@yourname
&lt;a name="rest_code_6a0eea4bd3314fc9a616fa6f7e7dd5d3-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;#&lt;/span&gt; systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; syncthing@yourname
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="section" id="configuration"&gt;
&lt;h3&gt;Configuration&lt;/h3&gt;
&lt;p&gt;Configuration is located at &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;~/.config/syncthing/&lt;/span&gt;&lt;/code&gt;.
The directory and config files inside it will be created
when Syncthing is first started.
It is described in detail in the &lt;a class="reference external" href="https://discourse.syncthing.net/c/documentation"&gt;Syncthing documentation&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/syncthing-on-arch-linux/#id3" id="id4"&gt;2&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code class="docutils literal"&gt;config.xml&lt;/code&gt; can either be edited via the web GUI
or manually.
By default, the Web GUI is only reachable from the same machine.&lt;/p&gt;
&lt;p&gt;If Syncthing is installed on a machine without a desktop, one can either
edit the configuration manually for &lt;em&gt;all&lt;/em&gt; tasks or at least to make
the GUI available on the network.
Manual configuration will also be necessary if you want to change the
port number under which the GUI is available, for example when
the default port &lt;strong&gt;8080&lt;/strong&gt; is already in use.&lt;/p&gt;
&lt;pre class="code xml"&gt;&lt;a name="rest_code_65f8dff76bce410bab2fc9a094813f06-1"&gt;&lt;/a&gt;&lt;span class="nt"&gt;&amp;lt;configuration&lt;/span&gt; &lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;"6"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_65f8dff76bce410bab2fc9a094813f06-2"&gt;&lt;/a&gt;    ...
&lt;a name="rest_code_65f8dff76bce410bab2fc9a094813f06-3"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;&amp;lt;gui&lt;/span&gt; &lt;span class="na"&gt;enabled=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt; &lt;span class="na"&gt;tls=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_65f8dff76bce410bab2fc9a094813f06-4"&gt;&lt;/a&gt;        &lt;span class="c"&gt;&amp;lt;!-- default: 127.0.0.1:8080 --&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_65f8dff76bce410bab2fc9a094813f06-5"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;&amp;lt;address&amp;gt;&lt;/span&gt;0.0.0.0:8888&lt;span class="nt"&gt;&amp;lt;/address&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_65f8dff76bce410bab2fc9a094813f06-6"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;&amp;lt;/gui&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_65f8dff76bce410bab2fc9a094813f06-7"&gt;&lt;/a&gt;    ...
&lt;a name="rest_code_65f8dff76bce410bab2fc9a094813f06-8"&gt;&lt;/a&gt;&lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;The first thing you might want to do is to remove the &lt;code class="docutils literal"&gt;~/Sync/&lt;/code&gt; directory
that was created on startup and replace it with something else.&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_e3f568cbcc9748fca998ee6aeb90f2e2-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;#&lt;/span&gt; systemctl stop syncthing@yourname
&lt;a name="rest_code_e3f568cbcc9748fca998ee6aeb90f2e2-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; rm -r ~/Sync
&lt;/pre&gt;&lt;p&gt;Edit the config file to include the directories to sync:&lt;/p&gt;
&lt;pre class="code xml"&gt;&lt;a name="rest_code_7c699ff2a69d4aac951719e7adf65be2-1"&gt;&lt;/a&gt;&lt;span class="nt"&gt;&amp;lt;configuration&lt;/span&gt; &lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;"6"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_7c699ff2a69d4aac951719e7adf65be2-2"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;&amp;lt;folder&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"default"&lt;/span&gt; &lt;span class="na"&gt;path=&lt;/span&gt;&lt;span class="s"&gt;"/home/yourname/sync-folder"&lt;/span&gt; &lt;span class="na"&gt;ro=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt; &lt;span class="na"&gt;rescanIntervalS=&lt;/span&gt;&lt;span class="s"&gt;"60"&lt;/span&gt; &lt;span class="na"&gt;ignorePerms=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_7c699ff2a69d4aac951719e7adf65be2-3"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;&amp;lt;device&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"[ID-1]"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/device&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_7c699ff2a69d4aac951719e7adf65be2-4"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;&amp;lt;versioning&amp;gt;&amp;lt;/versioning&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_7c699ff2a69d4aac951719e7adf65be2-5"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;&amp;lt;lenientMtimes&amp;gt;&lt;/span&gt;false&lt;span class="nt"&gt;&amp;lt;/lenientMtimes&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_7c699ff2a69d4aac951719e7adf65be2-6"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;&amp;lt;copiers&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/copiers&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_7c699ff2a69d4aac951719e7adf65be2-7"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;&amp;lt;pullers&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/pullers&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_7c699ff2a69d4aac951719e7adf65be2-8"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;&amp;lt;finishers&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/finishers&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_7c699ff2a69d4aac951719e7adf65be2-9"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;&amp;lt;/folder&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_7c699ff2a69d4aac951719e7adf65be2-10"&gt;&lt;/a&gt;    ...
&lt;a name="rest_code_7c699ff2a69d4aac951719e7adf65be2-11"&gt;&lt;/a&gt;&lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Create more &lt;code class="docutils literal"&gt;&amp;lt;folder&amp;gt;&lt;/code&gt; elements if you want to sync other directories.&lt;/p&gt;
&lt;p&gt;Changes will take effect when Syncthing is restarted:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_73a3a50c7f8c4e68a9a2468354e2bcb5-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;#&lt;/span&gt; systemctl restart syncthing@yourname
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="section" id="start-syncing"&gt;
&lt;h3&gt;Start Syncing&lt;/h3&gt;
&lt;p&gt;Repeat installation and configuration on other machines
you which to keep in sync.
On each machine, Syncthing must be started at least once to determine the
&lt;em&gt;device ID&lt;/em&gt; for that machine.&lt;/p&gt;
&lt;p&gt;Edit the config file to include the new devices
and add the device to each folder you want to sync:&lt;/p&gt;
&lt;pre class="code xml"&gt;&lt;a name="rest_code_41e3d547230240b680f6cb1ee1f9f12a-1"&gt;&lt;/a&gt;&lt;span class="nt"&gt;&amp;lt;configuration&lt;/span&gt; &lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;"6"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_41e3d547230240b680f6cb1ee1f9f12a-2"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;&amp;lt;folder&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"notes"&lt;/span&gt; &lt;span class="na"&gt;path=&lt;/span&gt;&lt;span class="s"&gt;"/home/akeil/notes"&lt;/span&gt; &lt;span class="na"&gt;ro=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt; &lt;span class="na"&gt;rescanIntervalS=&lt;/span&gt;&lt;span class="s"&gt;"60"&lt;/span&gt; &lt;span class="na"&gt;ignorePerms=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_41e3d547230240b680f6cb1ee1f9f12a-3"&gt;&lt;/a&gt;        &lt;span class="c"&gt;&amp;lt;!-- the ID of the local device --&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_41e3d547230240b680f6cb1ee1f9f12a-4"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;&amp;lt;device&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"[ID-1]"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/device&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_41e3d547230240b680f6cb1ee1f9f12a-5"&gt;&lt;/a&gt;        &lt;span class="c"&gt;&amp;lt;!-- one or more IDs of remote devices --&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_41e3d547230240b680f6cb1ee1f9f12a-6"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;&amp;lt;device&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"[ID-2]"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/device&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_41e3d547230240b680f6cb1ee1f9f12a-7"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;&amp;lt;versioning&amp;gt;&amp;lt;/versioning&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_41e3d547230240b680f6cb1ee1f9f12a-8"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;&amp;lt;lenientMtimes&amp;gt;&lt;/span&gt;false&lt;span class="nt"&gt;&amp;lt;/lenientMtimes&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_41e3d547230240b680f6cb1ee1f9f12a-9"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;&amp;lt;copiers&amp;gt;&lt;/span&gt;1&lt;span class="nt"&gt;&amp;lt;/copiers&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_41e3d547230240b680f6cb1ee1f9f12a-10"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;&amp;lt;pullers&amp;gt;&lt;/span&gt;16&lt;span class="nt"&gt;&amp;lt;/pullers&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_41e3d547230240b680f6cb1ee1f9f12a-11"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;&amp;lt;finishers&amp;gt;&lt;/span&gt;1&lt;span class="nt"&gt;&amp;lt;/finishers&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_41e3d547230240b680f6cb1ee1f9f12a-12"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;&amp;lt;/folder&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_41e3d547230240b680f6cb1ee1f9f12a-13"&gt;&lt;/a&gt;    &lt;span class="c"&gt;&amp;lt;!-- the local device --&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_41e3d547230240b680f6cb1ee1f9f12a-14"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;&amp;lt;device&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"[ID-1]"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"other-machine"&lt;/span&gt; &lt;span class="na"&gt;compression=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt; &lt;span class="na"&gt;introducer=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_41e3d547230240b680f6cb1ee1f9f12a-15"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;&amp;lt;address&amp;gt;&lt;/span&gt;dynamic&lt;span class="nt"&gt;&amp;lt;/address&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_41e3d547230240b680f6cb1ee1f9f12a-16"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;&amp;lt;/device&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_41e3d547230240b680f6cb1ee1f9f12a-17"&gt;&lt;/a&gt;    &lt;span class="c"&gt;&amp;lt;!-- remote devices to sync with --&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_41e3d547230240b680f6cb1ee1f9f12a-18"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;&amp;lt;device&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"[ID-1] name="&lt;/span&gt;&lt;span class="err"&gt;home"&lt;/span&gt; &lt;span class="na"&gt;compression=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt; &lt;span class="na"&gt;introducer=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_41e3d547230240b680f6cb1ee1f9f12a-19"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;&amp;lt;address&amp;gt;&lt;/span&gt;dynamic&lt;span class="nt"&gt;&amp;lt;/address&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_41e3d547230240b680f6cb1ee1f9f12a-20"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;&amp;lt;/device&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_41e3d547230240b680f6cb1ee1f9f12a-21"&gt;&lt;/a&gt;    ...
&lt;a name="rest_code_41e3d547230240b680f6cb1ee1f9f12a-22"&gt;&lt;/a&gt;&lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Restart syncthing:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_3772e375c48e442fbedceb3921952422-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;#&lt;/span&gt; systemctl restart syncthing@yourname
&lt;/pre&gt;&lt;div class="admonition note"&gt;
&lt;p class="admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;You must create &lt;code class="docutils literal"&gt;&amp;lt;device&amp;gt;&lt;/code&gt; elements on both machines.
(inside the &lt;code class="docutils literal"&gt;&amp;lt;folder&amp;gt;&lt;/code&gt;'s you wish to sync and in the general section
of known devices.).&lt;/p&gt;
&lt;p&gt;If not, entries like this appear in the log and nothing is synced:&lt;/p&gt;
&lt;pre class="literal-block"&gt;[...] Connection from 192.168.1.100:12345 with unknown device ID [ID-1];&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="logfiles-journal"&gt;
&lt;h4&gt;Logfiles (Journal)&lt;/h4&gt;
&lt;p&gt;View logs with:&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_3aa94d0ee5d5433bbfa468e8b6ec1c94-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; journalctl --unit syncthing@yourname
&lt;/pre&gt;&lt;p&gt;Or&lt;/p&gt;
&lt;pre class="code shell-session"&gt;&lt;a name="rest_code_8d13ff8eb0ea45988aa4dfd1c473176d-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; systemctl status syncthing@yourname
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="misc"&gt;
&lt;h3&gt;Misc&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;There is a &lt;a class="reference external" href="https://f-droid.org/repository/browse/?fdid=com.nutomic.syncthingandroid"&gt;Syncthing Android App&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/syncthing-on-arch-linux/#id7" id="id8"&gt;4&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;And &lt;a class="reference external" href="http://jasonwryan.com/blog/2014/05/10/syncthing/"&gt;another blog post&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/syncthing-on-arch-linux/#id9" id="id10"&gt;5&lt;/a&gt; on Syncthing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;There is &lt;a class="reference external" href="http://archlinuxarm.org/packages?arch=&amp;amp;search=syncthing"&gt;Syncthing for Arch Linux ARM&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/syncthing-on-arch-linux/#id11" id="id12"&gt;6&lt;/a&gt; which means it can run on a &lt;a class="reference external" href="http://www.raspberrypi.org/"&gt;RaspberryPi&lt;/a&gt; &lt;a class="footnote-reference brackets" href="https://akeil.de/posts/syncthing-on-arch-linux/#id13" id="id14"&gt;7&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr class="docutils"&gt;
&lt;dl class="footnote brackets"&gt;
&lt;dt class="label" id="id1"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/syncthing-on-arch-linux/#id2"&gt;1&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="http://syncthing.net/"&gt;http://syncthing.net/&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id3"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/syncthing-on-arch-linux/#id4"&gt;2&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="https://discourse.syncthing.net/c/documentation"&gt;https://discourse.syncthing.net/c/documentation&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id5"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/syncthing-on-arch-linux/#id6"&gt;3&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="https://www.archlinux.org/packages/community/x86_64/syncthing/"&gt;https://www.archlinux.org/packages/community/x86_64/syncthing/&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id7"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/syncthing-on-arch-linux/#id8"&gt;4&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="https://f-droid.org/repository/browse/?fdid=com.nutomic.syncthingandroid"&gt;https://f-droid.org/repository/browse/?fdid=com.nutomic.syncthingandroid&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id9"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/syncthing-on-arch-linux/#id10"&gt;5&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="http://jasonwryan.com/blog/2014/05/10/syncthing/"&gt;http://jasonwryan.com/blog/2014/05/10/syncthing/&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id11"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/syncthing-on-arch-linux/#id12"&gt;6&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="http://archlinuxarm.org/packages?arch=&amp;amp;search=syncthing"&gt;http://archlinuxarm.org/packages?arch=&amp;amp;search=syncthing&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="label" id="id13"&gt;&lt;span class="brackets"&gt;&lt;a class="fn-backref" href="https://akeil.de/posts/syncthing-on-arch-linux/#id14"&gt;7&lt;/a&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;&lt;a class="reference external" href="http://www.raspberrypi.org/"&gt;http://www.raspberrypi.org/&lt;/a&gt;&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/div&gt;
&lt;/div&gt;</description><category>linux</category><category>raspi</category><guid>https://akeil.de/posts/syncthing-on-arch-linux/</guid><pubDate>Wed, 10 Dec 2014 20:40:00 GMT</pubDate></item></channel></rss>