<?xml version="1.0" encoding="UTF-8" ?>
  <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
      <title>Julien Desrosiers</title>
      <link>https://www.juliendesrosiers.com</link>
      <description>Where I talk about stuff I'm passionate about.</description>
      <generator>PHP</generator>
      <language>en-us</language>
      <lastBuildDate>Mon, 15 Jun 2026 16:16:40 +0000</lastBuildDate>

      <atom:link href="https://www.juliendesrosiers.com/feed.xml" rel="self" type="application/rss+xml" />


                                      <item>
          <title>Track Web pageviews with Prometheus</title>
            <link>https://www.juliendesrosiers.com/2026/03/11/track-web-page-views-with-prometheus.php</link>
            <pubDate>Wed, 11 Mar 2026 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2026/03/11/track-web-page-views-with-prometheus.php</guid>
            <description><![CDATA[<p>I wanted to try my new Prometheus setup with some useful data, and since I want
to learn how to use Prometheus, I thought it would be fun and useful to do this.</p>

<p>In case you're not familiar with it, <a href="https://prometheus.io">Prometheus</a> is a server app that gathers
metrics in a database. Metrics that are then displayed in some specialized
web app such as <a href="https://grafana.com">Grafana</a> (the most popular for this usage).</p>

<p>I won't go into details of how to install prometheus, since this can be found
in many places on the Web.</p>

<h2>The JavaScript</h2>

<p>Here's the JS code I've put on all my pages (in <code>all.js</code> at the top):</p>

<pre><code class="js">const worker = new Worker('/javascripts/worker.js');
worker.postMessage({ page: window.location.pathname });
</code></pre>

<p>and here's the content of my <code>worker.js</code>:</p>

<pre><code class="js">self.onmessage = async (e) =&gt; {
    const { page } = e.data;
    await fetch('/prom_collector.php', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ url: page }),
        keepalive: true, // survives page unload
    });
};
</code></pre>

<h3>BTW, why use JavaScript and not the backend (PHP) directly?</h3>

<p>Because as you may know, we live in an era where bots who crawl web pages are
legion. And those bots don't normally operate within the context of a (costly)
headless browser. Some do -- like <a href="https://developers.google.com/search/docs/crawling-indexing/googlebot">Googlebot</a> -- but at the time of writing this,
they don't execute workers, because that might be even costlier, for marginal
gains.</p>

<p>In short, I want metrics about real humans visiting my site. Otherwise I
would've used my Apache logs.</p>

<h2>The PHP</h2>

<p>Here's the PHP code (<code>prom_collector.php</code>) that's triggered by the worker.js:</p>

<pre><code class="php">&lt;?php
/*
 * This page is requested by the javascript on tracked pages
 */

$db = new PDO('sqlite:' . __DIR__ . '/pageviews.sqlite3');

$json = file_get_contents('php://input');
$data = json_decode($json, false);

// Only execute this when deploying to a new site:
// $db-&gt;exec('CREATE TABLE IF NOT EXISTS views (page TEXT PRIMARY KEY, count INTEGER DEFAULT 0)');

$page = $data-&gt;url ?? 'unknown';
if ($page === 'unknown') {
  die("unknown page");
}
$stmt = $db-&gt;prepare('INSERT INTO views(page, count) VALUES(?, 1)
                      ON CONFLICT(page) DO UPDATE SET count = count + 1');
$stmt-&gt;execute([$page]);

echo json_encode(['ok' =&gt; true]);
</code></pre>

<p>And here's the code for the <code>metrics.php</code> page:</p>

<pre><code class="php">&lt;?php
/*
 * Prometheus scrapes this page
 */

$db = new PDO('sqlite:' . __DIR__ . '/pageviews.sqlite3');
$rows = $db-&gt;query('SELECT page, count FROM views')-&gt;fetchAll(PDO::FETCH_ASSOC);

header('Content-Type: text/plain; version=0.0.4');

// Begin prometheus-formatted document:
echo "# HELP page_views_total Number of times a page was accessed\n";
echo "# TYPE page_views_total counter\n";
foreach ($rows as $row) {
    $safe = addslashes($row['page']);
    echo "page_views_total{page=\"{$safe}\"} {$row['count']}\n";
}
</code></pre>

<p>So, for each page that received at least one visit, we display the number of
visits it got so far.</p>

<h2>Prometheus</h2>

<p>Finally, here's the config that I added to my <code>prometheus.yml</code>, under
<code>scrape_config</code>:</p>

<pre><code class="yaml">  - job_name: 'juliendesrosiers_com'
    scrape_interval: 60s
    static_configs:
      - targets: ['juliendesrosiers.com']
    metrics_path: '/metrics.php'
</code></pre>

<p>Then, restart <code>prometheus</code>.</p>

<p>The rest is <em>ClickOps</em>:</p>

<ol>
<li>login to your grafana</li>
<li>go to "Dashboards", then create a new dashboard -- if needed: <img src="https://img.desrosiers.org/257a66845863b7a5a89caa50738b9d29.png" alt="grafana dashboards section" /></li>
<li>click "Add visualization": <img src="https://img.desrosiers.org/15403e2f4c311d307c38d4a7391e1537.png" alt="grafana add visualization" /></li>
<li>choose "Prometheus" as the data source: <img src="https://img.desrosiers.org/7b1670138afb79d0011b5c9f479b8f0e.png" alt="grafana data source" /></li>
<li>now, scroll down and click "Select metric": <img src="https://img.desrosiers.org/c19760b60e50105738a6a664309b97db.png" alt="grafana select metric field" /></li>
<li>you should find the <code>page_views_total</code> (see <code>metrics.php</code>): <img src="https://img.desrosiers.org/175afebbeb87a9fa4b03105bd715abef.png" alt="grafana metric selection" /></li>
<li>select a label, if you want to group only for one site: <img src="https://img.desrosiers.org/fe5eb36a625a979c26137e5a18383c84.png" alt="grafana instance filtering" /></li>
<li>you can try the query by clicking "Run query", and if you're like me, you
have many page in your site. So each of those get assigned its own color:
<img src="https://img.desrosiers.org/ce2c9b8d4dbe0c9ffd8df4a747b84c9e.png" alt="grafana run query preview" /></li>
<li>which of course should get crowdy pretty quickly. so let's add another
operation, to aggregate those into only one metric (the total for all the
pages): <img src="https://img.desrosiers.org/720774abb0c370192663134dee46b629.png" alt="grafana SUM aggregation" /></li>
<li>"Run query" again, and you should now see something like this: <img src="https://img.desrosiers.org/1daae7446fbb0c447ef1fc6036a55e1d.png" alt="grafana preview, sum of pages" /></li>
<li>Make sure you save your work... <img src="https://img.desrosiers.org/8cdf376adb40b859d818e3210cfd5aaf.png" alt="grafana save dashboard button" /></li>
<li>Take the time to give a proper name for this dashboard:
<img src="https://img.desrosiers.org/4550cef47207069de1c8b4ef7b3cb9a3.png" alt="grafana name dashboard" /></li>
<li>Now go back to your dashboard view and take a look. Nice!: <img src="https://img.desrosiers.org/1bb9163630fff264dbca3cfff78e4851.png" alt="grafana dashboard" /></li>
</ol>

<h2>Downside of this approach</h2>

<ul>
<li>As it is right now, it won't allow much more granularity, when you compare to tools meant for
tracking visits, like Google Analytics. For example, adding viewport size and user
agent wouldn't scale well here.</li>
<li>I'm hosting Prometheus myself, so it's more expensive than using Google's
solution. (Which uses Cookies, which would necessitate adding one of those
pesky cookie acceptance popups)</li>
</ul>
]]></description>
          </item>
                                <item>
          <title>Minimum viable OpenID Connect with PHP</title>
            <link>https://www.juliendesrosiers.com/2025/04/05/minimum-viable-oauth2-login-with-php.php</link>
            <pubDate>Sat, 05 Apr 2025 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2025/04/05/minimum-viable-oauth2-login-with-php.php</guid>
            <description><![CDATA[<p>As we've seen in a <a href="/2025/03/22/host-keycloak-server.php">previous post</a>, you can install your own SSO, using Keycloak.</p>

<p>In this post, we'll see how you can connect to that SSO using only one page of
PHP.</p>

<h2>The dependencies</h2>

<p>Install these composer packages:</p>

<pre><code>composer install stevenmaguire/oauth2-keycloak
composer install firebase/php-jwt
</code></pre>

<h2>The code</h2>

<p>Put this in your <code>index.php</code> at the root of your publicly-available web folder:</p>

<pre><code class="php">&lt;?php

require 'vendor/autoload.php';

use Stevenmaguire\OAuth2\Client\Provider\Keycloak;
use Firebase\JWT\JWT;
use Firebase\JWT\JWK;

// error_reporting(E_ALL);
// ini_set('display_errors', 1);

ini_set('session.gc_maxlifetime', 1440);
ini_set('session.cookie_lifetime', 0);
ini_set('session.cookie_secure', 1);
ini_set('session.cookie_httponly', 1);

session_start();

$keycloak_base_url = 'https://sso.yourdomain.com';
$realm_slug = 'family';
$jwksUrl = "$keycloak_base_url/realms/$realm_slug/protocol/openid-connect/certs";
$provider = new Keycloak([
    'authServerUrl'         =&gt; $keycloak_base_url,
    'realm'                 =&gt; $realm_slug,
    'clientId'              =&gt; 'yourclient_id',
    'clientSecret'          =&gt; '[your secret here]',
    'redirectUri'           =&gt; 'https://yourdomain.com/index.php',
    'encryptionAlgorithm'   =&gt; 'RS256',                                       // optional
    'encryptionKeyPath'     =&gt; '/some/path/to/your_keycloak_privatekey.pem',  // optional
    'encryptionKey'         =&gt; 'contents_of_key_or_certificate',              // optional
]);

if (isset($_GET['action']) &amp;&amp; $_GET['action'] === 'logout') {
  unset($_SESSION['access_token']);
  unset($_SESSION['decoded_jwt']);
  header('Location: /index.php');
}

if (!isset($_GET['code'])) {

    // If we don't have an authorization code then get one
    $authUrl = $provider-&gt;getAuthorizationUrl();
    $_SESSION['oauth2state'] = $provider-&gt;getState();
    header('Location: '.$authUrl);
    exit;

// Check given state against previously stored one to mitigate CSRF attack
} elseif (empty($_GET['state']) || ($_GET['state'] !== ($_SESSION['oauth2state'] ?? ''))) {

    unset($_SESSION['oauth2state']);
    exit('Invalid state, make sure HTTP sessions are enabled.');

} else {

    // Try to get an access token (using the authorization coe grant)
    try {
        $token = $provider-&gt;getAccessToken('authorization_code', [
            'code' =&gt; $_GET['code']
        ]);
    } catch (Exception $e) {
        exit('Failed to get access token: '.$e-&gt;getMessage());
    }

    // Optional: Now you have a token you can look up a users profile data
    try {

        // We got an access token, let's now get the user's details
        $user = $provider-&gt;getResourceOwner($token);

        // Use these details to create a new profile
        printf('Hello %s!', $user-&gt;getName());

    } catch (Exception $e) {
        exit('Failed to get resource owner: '.$e-&gt;getMessage());
    }

    // Use this to interact with an API on the users behalf
    $_SESSION['access_token'] = $token-&gt;getToken();


    $token = $_SESSION['access_token'] ?? null;

    if (!$token) {
        die("No access token found. Please log in.");
    }

    // Fetch Keycloak's public key (JWKS)
    $jwks = json_decode(file_get_contents($jwksUrl), true);
    $keys = JWK::parseKeySet($jwks);

    // Decode &amp; Verify the JWT
    try {
        $decoded = JWT::decode($token, $keys);
        $_SESSION['decoded_jwt'] = $decoded;
        echo "&lt;pre&gt;";
        print_r($decoded); // Token data
    } catch (Exception $e) {
        die("Invalid token: " . $e-&gt;getMessage());
    }
}
</code></pre>

<p>Make sure you set your own:</p>

<ul>
<li><code>$realm_slug</code></li>
<li><code>$keycloak_base_url</code></li>
<li>in <code>$provider</code> object:

<ul>
<li>the <code>'redirectUri'</code> property</li>
<li>the <code>'clientId'</code> property</li>
<li>the <code>'redirectUri'</code> property</li>
<li>the <code>'redirectUri'</code> property</li>
<li>(optional) the <code>'encryptionAlgorithm</code>'</li>
<li>(optional) the <code>'encryptionKeyPath</code>'</li>
<li>(optional) the <code>'encryptionKey</code>'</li>
</ul></li>
</ul>

<h1>Where to go from here</h1>

<ul>
<li>As you can see, we don't persist the token we get from the OAuth2 service.
So you might want to add some session + cookie persistence in there.</li>
<li>You'll also want to do something else than simply print out the content of
your <code>JWT</code> in your page.</li>
<li>And add proper error logging instead of displaying everything to the users.</li>
</ul>
]]></description>
          </item>
                                <item>
          <title>Host your own Keycloak SSO</title>
            <link>https://www.juliendesrosiers.com/2025/03/22/host-keycloak-server.php</link>
            <pubDate>Sat, 22 Mar 2025 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2025/03/22/host-keycloak-server.php</guid>
            <description><![CDATA[<h2>First, you'll need Keycloak.</h2>

<h3>Server specs requirements</h3>

<p>I'm installing this on Ubuntu 24.04.</p>

<p>Look at <a href="https://www.keycloak.org/high-availability/concepts-memory-and-cpu-sizing">this page for more details</a>.
TL;DR: Have at least 2 vCPU and 2 GB of RAM for a start.</p>

<h3>Installing Keycloak</h3>

<p>First need to install the dependencies</p>

<p>Start here:
https://www.keycloak.org/getting-started/getting-started-zip</p>

<p>Basically you'll:</p>

<pre><code># install software
sudo apt install openjdk-21-jdk wget unzip -y
# get keycloak
wget https://github.com/keycloak/keycloak/releases/download/26.1.4/keycloak-26.1.4.zip
unzip keycloak-26.1.4.zip
cd keycloak-26.1.4

# execute keycloak:
# WARNING: this is the development version of keycloak.
# DO NOT USE this in production.
bin/kc.sh start-dev

# When we're ready we'll use this instead:
# bin/kc.sh start

</code></pre>

<p>Now you cana take a look at your host, on port 8080:
<code>http://&lt;your-hostname-or-ip&gt;:8080</code></p>

<p>If you see something like this, that's a good sign:</p>

<p><img src="https://img.desrosiers.org/d7d6e4bf1161f12b37a029dfb22ff1f2.png" alt="screenshot of Keycloak's &quot;Local access required&quot; message" /></p>

<p>But we won't do the <code>bootstrap-admin</code> command just yet. First, let's make our
setup more production-ready instead.</p>

<h3>TLS</h3>

<p>You'll need a TLS cert, so get <a href="https://certbot.eff.org/">certbot</a> for <a href="https://letsencrypt.org/">Let's Encrypt</a> certificates:</p>

<pre><code>sudo apt install certbot -y
# And since I use nginx:
sudo apt install nginx python3-certbot-nginx -y
sudo systemctl start nginx
# Now, confidure the certificate:
sudo certbot --nginx
</code></pre>

<p>When you're done setting up your certificate, you can terminate nginx since you
won't need it anymore:</p>

<pre><code>sudo systemctl stop nginx
</code></pre>

<h2>PostgreSQL</h2>

<p>It's also recommended that you use postgresql as a db for storing Keycloak's
data. So let's install it.</p>

<pre><code>sudo apt install postgresql -y
</code></pre>

<p>Then change the default password by doing this:</p>

<pre><code># open a postgresql client session:
sudo -u postgres psql
# then in your postgres client session:
ALTER USER postgres WITH PASSWORD '...';
# then to quit:
\q
</code></pre>

<p>Then connect with your postgres and create the database:</p>

<pre><code>CREATE DATABASE keycloak;
GRANT ALL PRIVILEGES ON DATABASE keycloak TO postgres;
# then quit:
\q
</code></pre>

<h2>Configure Keycloak</h2>

<p>In the keycloak folder you unzipped earlier, you will edit the following file:</p>

<p><code>conf/keycloak.conf</code></p>

<p>And put the following (modified to suit your own info):</p>

<pre><code># Database
db=postgres
db-username=postgres
db-password=secret
db-url=jdbc:postgresql://localhost/keycloak_db

# HTTP
https-certificate-file=/etc/letsencrypt/live/yourdomain.com/fullchain.pem
https-certificate-key-file=/etc/letsencrypt/live/yourdomain.com/privkey.pem
https-port=443

hostname=yourdomain.com
</code></pre>

<p>Notes:</p>

<ul>
<li><p>Replace <code>yourdomain.com</code> with your actual keycloak domain name</p></li>
<li><p>Also, Don't surround your <code>db-password</code> with quotes, or those will actually be part
of the <code>db-password</code>. You can put anything after the <code>=</code> symbol, until the end of
the line, and it'll be part of the password.</p></li>
</ul>

<h1>Start the server in production mode:</h1>

<pre><code>cd bin/
kc.sh start
</code></pre>

<p>To show the configurations:</p>

<pre><code>kc.sh show-config
</code></pre>

<p>Now, you should be able to access to you domain, on port 8443 in your browser.</p>

<p>For example: https://yourdomain.com:8443
And it should display something like this:</p>

<p><img src="https://img.desrosiers.org/d2ed3f19bcd56317643a7107e0f7a038.png" alt="Keycloak welcome screen" /></p>

<p>which says among other things, to execute the <code>bootstrap-admin</code> command.</p>

<p>So, in another SSH session, we'll go into the keycloak folder that we
downloaded earlier and do:</p>

<pre><code>bin/kc.sh bootstrap-admin user
# and follow the steps...
</code></pre>

<p>Now, restart keycloak by going to the SSH session where you started the <code>kc.sh
start</code> command, and do CTRL-c, to end the process.
And now, do:</p>

<pre><code>bin/kc.sh start --optimized
</code></pre>

<p>You should now create a permanent admin user.</p>

<p>Go to the Users section in the left menu.</p>

<p>Fill in the details. And create the user.
Then go to the Role mapping tab at the top. Click Assign role.
Click the "Filter by realm roles" button, and you should see <code>admin</code> and a few
others appear. Select those, and click Assign.</p>

<p>Test your newly created user, and when you're satisfied, delete the temporary
user you created previously.</p>

<h1>Create your own realm</h1>

<p>At the upper left, click "Keycloak - master". Then click "create realm".</p>

<p>Enter a name.</p>

<p>Click Create.</p>

<h2>Add a client to this realm</h2>

<blockquote>
  <p>Clients are applications and services that can request authentication of a
  user.</p>
</blockquote>

<ul>
<li><strong>Client type</strong>: OpenID Connect</li>
<li><strong>Client ID</strong>: (The client identifier registered with the identity provider.)<br>
Or if you use Keycloak as your IdP, enter anything you want here.</li>
<li>Click "Next" (to <strong>Capability config</strong>)

<ul>
<li><strong>Client authentication</strong>: if your client is a backend app, tick this to "ON"</li>
<li><strong>Authentication flow</strong>: Make sure you check "Implicit flow", as well as "Standard flow" and "Direct
access grants".</li>
</ul></li>
<li>Click "Next" (to <strong>Login settings</strong>)

<ul>
<li><strong>Root URL</strong>: your hostname without the ending slash (https://someclient.com)</li>
<li><strong>Home URL</strong>: your hostname <em>with</em> the ending slash (https://someclient.com/)</li>
<li><strong>Valid redirect URIs</strong>: Something like: <code>https://someclient.com/*</code></li>
<li><strong>Valid post logout redirect URIs</strong>: <code>+</code></li>
<li><strong>Web origins</strong>: <code>+</code></li>
</ul></li>
<li>click Save</li>
</ul>

<h2>Create yourself a new user</h2>

<p>In the left sidebar, click Users, then "Create new user".</p>

<ul>
<li><strong>Required user actions</strong>: it's really up to you, but if you create it for someone
else, I suggest you choose "Update Password" or "Verify Email".</li>
<li><strong>Email verified</strong>: If it's for yourself, you can check this to "On" already.</li>
<li><strong>Username</strong>, etc.</li>
<li>Then "Create".</li>
<li>Now, go to "Credentials" tab and click "Set password"</li>
<li>Enter your desired password, and if it's for you, again, you can check
"Temporary" to <strong>Off</strong> if you want.
Then "Save".</li>
</ul>

<h2>Conclusion</h2>

<p>That's it!</p>

<p>In an <a href="https://www.juliendesrosiers.com/2025/04/05/minimum-viable-oauth2-login-with-php.php">upcoming
post</a>, I'll show you how you can log in to this Identity Provider
(IdP) using PHP.</p>
]]></description>
          </item>
                                                            <item>
          <title>Configuring XDebug for VS Code over SSH</title>
            <link>https://www.juliendesrosiers.com/2022/09/13/php-xdebug-vscode-remote-ssh.php</link>
            <pubDate>Tue, 13 Sep 2022 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2022/09/13/php-xdebug-vscode-remote-ssh.php</guid>
            <description><![CDATA[[...]]]></description>
          </item>
                                <item>
          <title>Top 10 skills every programmer should know</title>
            <link>https://www.juliendesrosiers.com/2022/09/03/10-skills-programmers.php</link>
            <pubDate>Sat, 03 Sep 2022 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2022/09/03/10-skills-programmers.php</guid>
            <description><![CDATA[[...]]]></description>
          </item>
                                              <item>
          <title>The Slow Web</title>
            <link>https://www.juliendesrosiers.com/2022/01/27/slow-web.php</link>
            <pubDate>Thu, 27 Jan 2022 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2022/01/27/slow-web.php</guid>
            <description><![CDATA[[...]]]></description>
          </item>
                                                                          <item>
          <title>Tools for thinking</title>
            <link>https://www.juliendesrosiers.com/2021/08/21/tools-for-thinking.php</link>
            <pubDate>Sat, 21 Aug 2021 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2021/08/21/tools-for-thinking.php</guid>
            <description><![CDATA[<p><img src="/2021/08/21/tools-for-thinking-header.jpg" alt="Tools for thinking" /></p>

<p>Fact: I need exo-brain (tech-)tools to help me make sense of complex things.</p>

<p>Here are some of them:</p>

<h2>1. Kinopio</h2>

<p><a href="https://kinopio.club/">Kinopio</a> is quite fun to use. Here are a few examples:</p>

<h3>For philosophy</h3>

<p><a href="https://img.desrosiers.org/eb5be3e94325a15ee674be5c9bee52c4.png"><img src="https://img.desrosiers.org/eb5be3e94325a15ee674be5c9bee52c4.png" alt="kinopio for philosophy" /></a></p>

<p><a href="https://img.desrosiers.org/a1e0a4b02e2a142b3b11e62af696d1d3.png"><img src="https://img.desrosiers.org/a1e0a4b02e2a142b3b11e62af696d1d3.png" alt="kinopio for collecting concepts about
philosophy" /></a></p>

<h3>For political sciences</h3>

<p><a href="https://img.desrosiers.org/196c5305d82518e1321c44da50dff616.png"><img src="https://img.desrosiers.org/196c5305d82518e1321c44da50dff616.png" alt="political science with kinopio" /></a></p>

<h3>For linguistics</h3>

<p><a href="https://img.desrosiers.org/8be2b21637f71430b33ab38a417037d1.png"><img src="https://img.desrosiers.org/8be2b21637f71430b33ab38a417037d1.png" alt="kinopio for linguistic
thinking" /></a></p>

<h3>For collaborative brainstorming / moodboarding</h3>

<p>A nice alternative to pinterest:</p>

<p><a href="https://img.desrosiers.org/8822faf5f1e9ea2bd7daf5c880c41b55.png"><img src="https://img.desrosiers.org/8822faf5f1e9ea2bd7daf5c880c41b55.png" alt="kinopio as a pinterest
alternative" /></a></p>

<p>Credits to @tiff and @Liby: <a href="https://kinopio.club/tiff-n-liby-maximalist-clothing-brainstorm--yL1z3JKaC67bKqbHyxlWD">board's link</a></p>

<h3>For mindmapping</h3>

<p><a href="https://img.desrosiers.org/ce2a8b220d918f3d21bcf0e3366a6502.png"><img src="https://img.desrosiers.org/ce2a8b220d918f3d21bcf0e3366a6502.png" alt="clound native
mindmap" /></a></p>

<h2 id="batf">2. Big-Ass Text File</h2>

<p>One big, always open, <a href="https://www.ascentstage.com/archives/2005/08/one_big_ass_text_file-html/">big-a&#42;&#42; text file</a>.</p>

<p>The best metaphor I can find for conveying why this is such a great tool is
this: your short term memory is like RAM.</p>

<p>But when the RAM gets full (depending on your IQ and how much sleep you had), a
big-ass-text-file is where you can dump whatever is too much for you to
efficiently remember for your work. It's your Swap file.
And just like Swap, it's slow to write on and read from. But it can be
convenient.</p>

<p>Because as humans, our brain is not always optimized to retain much
data and it gets overwhelmed quickly if you give it too much.</p>

<p>I use Notepad++ for this. Mainly because I don't want to use the same text editor as I use for coding (VS Code + Vim, right now).</p>

<p>For me, it serves many purposes, among which:</p>

<ol>
<li>a paste board for easy-to-recover snippets of code or data</li>
<li>a paste board for order confirmation numbers after purchasing stuff</li>
<li>a more comfortable (multi-line) place to edit throwaway-code like SQL queries or javascript
to execute in the web console, or long bash commands when I'm too lazy to
create script files.</li>
<li>a temporary place when someone gives me a bunch of project-related passwords, before properly storing them in my password manager.</li>
<li>a place where I paste long stack traces that need to be unwrapped for easier reading.</li>
<li>a place for writing long-form instant messaging responses outside of the non-standard web interfaces with inconsistent keyboard shortcuts.</li>
<li>a repository of fleeting notes that may become permanent notes in my zettelkasten</li>
</ol>

<h3>Some examples:</h3>

<p><a href="https://img.desrosiers.org/be30030d3d090551c0d2a172e02fb5e6.png"><img src="https://img.desrosiers.org/be30030d3d090551c0d2a172e02fb5e6.png" alt="metasploit session" /></a>
(a metasploit session for future reference)</p>

<p><a href="https://img.desrosiers.org/f469172b8442468960d36a828366baeb.png"><img src="https://img.desrosiers.org/f469172b8442468960d36a828366baeb.png" alt="code" /></a>
(some dumped ruby object and an SQL query for a WordPress site)</p>

<p>Note: I try to put the current date every morning when using it, in order to
find my notes by date later on, but this is not required, since you can have a
sense of time due to the fact that the most recent content is at the bottom of
the file (or the top, depending on your preference).</p>

<h2>3. Big-Ass SpreadSheet</h2>

<p>It's a variant of the BATF, but for tabular data.</p>

<p>The key factor to consider here is the <strong>cost</strong>.</p>

<p>You see, when you want to quickly dabble with tabular data, you normally...</p>

<ol>
<li>open Excel</li>
<li>create a new spreadsheet</li>
<li>(want to save it?) navigate to the right place on your drive where you want
it to be saved.</li>
</ol>

<p>Now, with the 
<ruby>
B <rp>(</rp><rt>Big-</rt><rp>)</rp>
A <rp>(</rp><rt>Ass</rt><rp>)</rp>
S <rp>(</rp><rt>Spread</rt><rp>)</rp>
S <rp>(</rp><rt>Sheet</rt><rp>)</rp>
</ruby>
, those steps are just one click away:
Click the <code>+</code> button in your (always-open spreadsheet software) to create a new sheet.</p>

<p>Boom: Tabular notes are now <strong>cheap</strong>!</p>

<p>🌼Create as many as you want.🌼</p>

<h3>Some examples:</h3>

<p>All those times you're doing
<a href="https://en.wikipedia.org/wiki/Cross-multiplication">cross-multiplication</a> calculations:</p>

<p><img src="https://img.desrosiers.org/33155ea53f1fdaef3bf6fc136c37c547.png" alt="Cross products using excel" /></p>

<p>Ad-hoc comparison lists:</p>

<p><a href="https://img.desrosiers.org/391ef94dd5eabb2d45607be786a6e65b.png"><img src="https://img.desrosiers.org/391ef94dd5eabb2d45607be786a6e65b.png" alt="comparison lists in excel" /></a>
(what book should I read next?)</p>

<p>Health log:</p>

<p><a href="https://img.desrosiers.org/14df1c7de217113bef8baf5c6d2f6011.png"><img src="https://img.desrosiers.org/14df1c7de217113bef8baf5c6d2f6011.png" alt="BASS" /></a>
(As you can see, I have a somewhat non-existant sleep hygiene)</p>

<h2>4. Joplin</h2>

<p>Joplin is great for notes about specific topics and linking them together, and
grouping them by tags and notebooks. Pretty much like Evernote.</p>

<p>But unlike Evernote, it has Markdown support and code highlighting. And it's
free.</p>

<p>I use Joplin as my personal
<a href="https://en.wikipedia.org/wiki/Zettelkasten">Zettelkasten</a> tool.</p>

<p><a href="https://img.desrosiers.org/da97d81cbbbee39ac1c75ddc8a5eb9a5.png"><img src="https://img.desrosiers.org/da97d81cbbbee39ac1c75ddc8a5eb9a5.png" alt="Learning" /></a>
(As you can see, I <em>just started</em> learning Vue.js 😅)</p>

<p><a href="https://img.desrosiers.org/7ba948aea21b469b6db2bd48da5e2221.png"><img src="https://img.desrosiers.org/7ba948aea21b469b6db2bd48da5e2221.png" alt="Reading
notes" /></a>
(My reading notes of <a href="https://www.amazon.ca/Good-Great-Some-Companies-Others/dp/0066620996">Good to
Great</a>)</p>

<h2>Conclusion</h2>

<p>In order for a tool to be <em>useful</em>, it must be <em>handy</em> (at-hand). 
And I've found that the principles outlined in the book <a href="https://jamesclear.com/atomic-habits">Atomic Habits</a> are pretty helpful for this:</p>

<ol>
<li>Make It <strong>Obvious</strong> (an always-open app is just that)</li>
<li>Make it <strong>Attractive</strong> (Kinopio is kinda attractive, don't you find? ;-))</li>
<li>Make it <strong>Easy</strong> (Stupid-simple to use, Obvious UX, Lightweight)</li>
<li>Make It <strong>Satisfying</strong> (It must do the job of storing things in a satisfying way. And it's pretty
satisfying when you eventually find info that you need, by just <code>CTRL-F</code>'ing
for it.)</li>
</ol>
]]></description>
          </item>
                                <item>
          <title>Self-Help, abridged.</title>
            <link>https://www.juliendesrosiers.com/2021/08/07/self-help.php</link>
            <pubDate>Sat, 07 Aug 2021 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2021/08/07/self-help.php</guid>
            <description><![CDATA[<p><img src="/2021/08/07/self-help-abridged-header.jpg" alt="woman happy in front of a computer sitting on the grass in a sunny day, by DALL-E" /></p>

<p>Sometimes, I feel like I’m a self-help guru.</p>

<p>I mean, I know everything there is to know about self-help. Don’t believe me? Here is a pretty thorough list of things that are taught in 80% of self-help books. 
Note: This list has contradictions in it. This is because there are many opinions in the field of self-help and productivity.</p>

<ul>
<li>Have a "morning routine"

<ul>
<li>make your bed first thing in the morning

<ul>
<li><code>#QuickWin</code>, <code>#InstantGratification</code>, <code>#start-your-day-on-the-right-foot</code></li>
</ul></li>
<li>avoid coffee and alcohol.

<ul>
<li>These substances generate dopamine in your brain, which breaks your craving for more natural ways (like exercise and meditation) of getting that dopamine hit.</li>
</ul></li>
<li>don’t watch your phone first thing when you wake up. In fact, start your day in airplane mode.</li>
<li>do something with your body to get some blood in your brain and get some oxygen</li>
<li>take a cold shower (<a href="https://www.wimhofmethod.com/iceman-wim-hof">Iceman</a>, we all know you by now)</li>
</ul></li>
<li>Meditate

<ul>
<li>Practice mindfulness meditation</li>
<li>Visualize your day. Visualize how you’re going to tackle your most significant task.</li>
</ul></li>
<li>Move your body

<ul>
<li>Take a walk</li>
<li>Practice a sport. Jogging, bike, whatevs.</li>
</ul></li>
<li>Start working

<ul>
<li>Ideally, you’re still in your “power hour” by now, which is the first hour of your day, while your brain is at its freshest state.</li>
<li>To determine the task to do, use the
<a href="https://productiveclub.com/eisenhower-matrix/">Heizenhower matrix</a></li>
<li>Break your tasks into smaller, more easily manageable sub-tasks</li>
<li>Find your one task that absolutely needs to get done today. Your most significant one. This is the “<a href="https://www.amazon.ca/Eat-That-Frog-Great-Procrastinating/dp/162656941X">frog</a>” that you’ll eat.</li>
<li>Use the Pomodoro technique to get going with your small sub-tasks.</li>
</ul></li>
<li>Eat a good, healthy meal</li>
<li>Go to the cafe to focus

<ul>
<li>Your brain is <a href="https://en.wikipedia.org/wiki/Antifragile">antifragile</a> to ambient noise as it helps your focus. But don’t drink coffee there!</li>
<li>Instead of coffee, take a cup of white tea. It’s good for your health. :-)</li>
</ul></li>
<li>Practice Deep work

<ul>
<li>Emails

<ul>
<li>Use the <a href="https://www.howtogeek.com/413507/what-is-inbox-zero-and-how-can-you-achieve-it/">inbox
zero</a> philosophy</li>
<li>Open your mailbox only one or two times per day.</li>
</ul></li>
<li>Avoid <a href="https://twitter.com/hellodesro">social networks</a> as much as possible</li>
</ul></li>
<li>Manage your time

<ul>
<li>Use a calendar app, like Google Calendar</li>
<li>Use a todo list app, like Todoist</li>
</ul></li>
<li>Manage your physical space

<ul>
<li>Read Marie Kondo’s “The Life-Changing Magic of Tidying Up” and tidy up your space

<ul>
<li>Only have useful things or things that bring you joy, in your surrounding, at all times.</li>
<li>Throw away pretty much everything else.</li>
</ul></li>
</ul></li>
<li>Sleep well and sleep enough

<ul>
<li>Have a bedtime routine

<ul>
<li>Go to bed at regular hours every night</li>
<li>Avoid screens 1-2 hours before bedtime</li>
<li>don’t drink too much</li>
</ul></li>
</ul></li>
<li>Create your mission/vision statement

<ul>
<li>Start with your <a href="https://simonsinek.com/product/start-with-why/">why</a></li>
</ul></li>
<li>Set goals

<ul>
<li>“If you don’t have a plan, you plan to fail”</li>
<li>Set <a href="https://corporatefinanceinstitute.com/resources/knowledge/other/smart-goal/">S.M.A.R.T.</a> goals</li>
</ul></li>
<li>Don’t set goals

<ul>
<li>“Everyone has a plan until they get punched in the mouth” -- Mike Tyson</li>
<li>Instead, have a <a href="https://www.scottadamssays.com/2013/11/18/goals-vs-systems/">system</a></li>
<li>A “system” is a context and a series of habits that you will follow that will lead you towards your vision</li>
</ul></li>
<li>Tell everyone about your goals, to create social pressure that will force you to reach your goals</li>
<li>Tell yourself positive things every morning in the mirror. Your subconscious will conform to those words.

<ul>
<li>Avoid negative statements like “I don’t like fast food”, but instead, tell yourself “I eat healthy foods” (see <a href="https://www.amazon.ca/-/fr/Anthony-Robbins/dp/0671610880/ref=tmm_hrd_swatch_0?_encoding=UTF8&amp;qid=&amp;sr=">Unlimited Power</a> for more details)</li>
</ul></li>
<li>Think like the stoïc philosophers

<ul>
<li><a href="https://en.wikipedia.org/wiki/Marcus_Aurelius">Marcus Aurelius</a></li>
<li><a href="https://en.wikipedia.org/wiki/Seneca_the_Younger">Seneca</a></li>
</ul></li>
<li>Harness the power of compound interest

<ul>
<li>Learn every day</li>
<li><a href="https://www.richdad.com/pay-yourself-first">Pay yourself first</a> by investing a bit every time you earn money</li>
<li><a href="https://www.iwillteachyoutoberich.com/blog/automating-your-accounts-video/">Automate</a> the money from your paycheck to your investment account, so you don’t have to take the decision of saving/investing</li>
<li>Invest in yourself by reading at least 1h every day. Bill Gates, Elon Musk, Mark
Zuckerberg, Oprah Winfrey, all <a href="https://www.inc.com/james-paine/5-billionaires-who-credit-their-success-to-reading.html">credit their success to
reading</a>. What you'll learn will compound by making you more productive, and/or by making links to other things you'll learn in order to make your creativity more fruitful.</li>
</ul></li>
<li>Reduce useless thinking

<ul>
<li>By dressing the same way every day (like Steve Jobs and Mark Zuckerberg)</li>
<li>By hiring a Virtual Assistant (VA) to handle your emails and do some other mundane tasks</li>
</ul></li>
<li>Time is money, so set your personal hourly rate and respect it

<ul>
<li>If some task could be done as efficiently by someone else (like a VA) at a lower hourly cost than yours, then delegate it</li>
</ul></li>
</ul>

<p>Now, you could read books, but in the end, it’s only a matter of being convinced about the truth of these advice. This is why people like me read books anyway, and they become super enthusiast about them because it makes them understand why those advice work. And when you encounter the same advice over and over, you tend to believe them because there seems to be a consensus in the self-help community, if you will.</p>

<h2>Conclusion</h2>

<p>You then attain objective truth and get meaning out of life.</p>

<p>And you win at everything.</p>

<p>You're welcome.</p>
]]></description>
          </item>
                                                            <item>
          <title>Developers need stats</title>
            <link>https://www.juliendesrosiers.com/2021/06/04/stats-web-developers.php</link>
            <pubDate>Fri, 04 Jun 2021 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2021/06/04/stats-web-developers.php</guid>
            <description><![CDATA[<h1>Web developers <strike>like</strike> love stats</h1>

<p>Today, I received a FWD of one of those Google Search Console email
reports.<br>
That's great. Here's why:</p>

<p><img src="stat-01.jpg" alt="stats" /></p>

<p><strong>0 page with first impression</strong> ?</p>

<p>There is still room for improvement.</p>

<p><img src="stat-02.jpg" alt="stat 2" /></p>

<p>Here, I can see what are the pages that receive the most traffic. As you can
see, it's not much, but it's nevertheless from those points that we should
work. It's nice to know that people look up portfolio client's info and land on our client's site.</p>

<p>That could be a good thing to measure in our analytics. Since that could even
guide us towards how to select our future clients (are they popular?). But I
digress...</p>

<p>That also means that if we're going to make a redesign for that page, it's going to
be useful to make sure we setup some redirections from those URLs to the new
ones, so we don't disturb incoming traffic coming from Google.</p>

<p><img src="stat-03.jpg" alt="stat 3" /></p>

<p>Here we can see the most popular keywords people use to get to that site.
Again, if we're doing a revamp of that site, make sure those keywords still
appear for the target pages we want people land on.</p>

<p>In our case, it's the name of the company, the market they're in
(architecture), and the name of one of their clients.</p>

<p><img src="stat-04.jpg" alt="stat 4" /></p>

<p>This should be a good indicator for where to put some QA love ;-)</p>

<p><img src="stat-05.jpg" alt="stat 5" /></p>

<p>Even though our client is from Québec, we don't know how many of these clicks
are coming from english-speaking parts of Canada. But it's good to consider
serving your content in english as well. (And in our case, that's exactly what
we're going to do ;-)</p>

<p><img src="stat-06.jpg" alt="stat 6" /></p>

<p>And here, that's interesting for us as we have a lot of content in the form of
images. That means we have an opportunity here to put <code>alt</code> attributes on our
images, to get more views in Google Image. :)</p>
]]></description>
          </item>
                                <item>
          <title>Switching to the Colemak keyboard layout</title>
            <link>https://www.juliendesrosiers.com/2021/05/10/switching-to-colemak.php</link>
            <pubDate>Mon, 10 May 2021 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2021/05/10/switching-to-colemak.php</guid>
            <description><![CDATA[<h2>Two weeks in</h2>

<p>I picked up Colemak gradually by mostly using <a href="https://www.colemak.academy/">colemak.academy</a>, for 62 5-7 practices, over 13 days.</p>

<p>I come from a QWERTY, ~42 WPM typing speed, that I got in 2005-ish, for my
computer graphics design degree.</p>

<p>Now, I feel very slow with my current record time of almost 18 WPM, and it
keeps getting faster. I only stopped using QWERTY, 4 days ago.</p>

<p>I still struggle mostly with the 'G', and to a lesser degree, with the 'U'
(which is at almost the same location).</p>

<p>But my top accuracy % for now is of 98.24%. Which is still not enough in my
opinion, since it still hinders too much my typing to remain into "the zone".</p>

<p><strong>Update</strong>: two days after writing this post, I can now say that even though
I'm still at 20 WPM, I no longer need my <a href="https://en.wikipedia.org/wiki/Thinking,_Fast_and_Slow#Two_systems">"system two"
thinking</a> as much as during the
beginning of my learning.</p>

<h2>Vim</h2>

<p>I'm pretty minimalistic for now regarding my Vim setup, but it works for me so
far.</p>

<p>Here is the part of my <code>.vimrc</code> that makes this move possible without much
trouble:</p>

<pre><code>" Navigation :
noremap n j
noremap e k
noremap i l
" End of word navigation :
noremap j e
" Next in search mode :
noremap k n
" l becomes the equivalent of i, for insert mode :
noremap l i

" Moving between splits :
noremap &lt;C-w&gt;n &lt;C-w&gt;j
noremap &lt;C-w&gt;e &lt;C-w&gt;k
noremap &lt;C-w&gt;i &lt;C-w&gt;l
</code></pre>

<p>The biggest thing to remember is that you'll no longer use the <code>i</code> key to enter
into insert mode, but <code>l</code> instead.</p>

<h3>NERDTree</h3>

<p>In Vim, I need to be able to navigate through the folders of my projects, and
since the 'e' key is now being used for navigation instead of 'k', and
NERDTree is using 'e' to explore the selected directory, I need to override
those:</p>

<pre><code class="viml">let NERDTreeMapOpenExpl="E" " free up the 'e' key to be used for something else
let NERDTreeMenuUp="e"
</code></pre>

<h2>Typing in french</h2>

<p>With Colemak it is possible to type common french accents like:</p>

<ul>
<li><code>ç</code> - with <code>AltGr + c</code></li>
<li><code>é</code> - with <code>AltGr + e</code></li>
<li><code>à</code> - with <code>AltGr + r</code>, then <code>a</code></li>
<li><code>ê</code> - with <code>AltGr + x</code>, then <code>e</code></li>
<li><code>ï</code> - with <code>AltGr + d</code>, then <code>i</code></li>
</ul>

<p>So for me, it replaces not only the English QWERTY, but also the French
Canadian QWERTY layout, as I often need to switch between the two when working
(which involves communicating in french and coding with my English US physical
(mechanical -- more on that in another post) keyboard.</p>

<h2>Conclusion</h2>

<p>It's too early to say that I'm more productive with that layout, but so far I
see a small but steady increase in speed. And I've noticed that it really helps
getting good sleep because I tend to get my best performances in the morning.</p>
]]></description>
          </item>
                                              <item>
          <title>Open Source, self-hosted SaaS alternatives</title>
            <link>https://www.juliendesrosiers.com/2021/03/13/open-source-self-hosted-saas-alternatives.php</link>
            <pubDate>Sat, 13 Mar 2021 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2021/03/13/open-source-self-hosted-saas-alternatives.php</guid>
            <description><![CDATA[<h2>When open source web apps compete with SaaS, it's interesting.</h2>

<p>After learning about Baserow on
<a href="https://news.ycombinator.com/item?id=26448985">HN</a>, I decided that I needed to
write about that trend in the open-source/startups world, to create an
alternative of X (SaaS) that is self-hosted, but also that you can pay to have hosted
for you, if you're not technically inclined.</p>

<p>Here is a list of high-quality products that I've seen so far using that
approach:</p>

<table>
<thead>
<tr>
  <th>SaaS</th>
  <th>Open, Self-Hosted Alternative</th>
</tr>
</thead>
<tbody>
<tr>
  <td>Google Drive</td>
  <td><a href="https://nextcloud.com/">Nextcloud</a></td>
</tr>
<tr>
  <td>Excel in Office 365</td>
  <td><a href="https://www.getgrist.com/pricing/">Grist</a></td>
</tr>
<tr>
  <td>LastPass</td>
  <td><a href="https://bitwarden.com/">Bitwarden</a>, <a href="https://github.com/dani-garcia/vaultwarden">Vaultwarden</a></td>
</tr>
<tr>
  <td>Notion</td>
  <td><a href="https://b3log.org/siyuan/">siyuan</a></td>
</tr>
<tr>
  <td>Vercel</td>
  <td><a href="https://coolify.io/">Coolify</a></td>
</tr>
<tr>
  <td>Pinboard / Del.icio.us</td>
  <td><a href="https://github.com/omnivore-app/omnivore">Omnivore</a></td>
</tr>
<tr>
  <td>Google Analytics</td>
  <td><a href="https://plausible.io/">Plausible</a>, <a href="https://umami.is/">umami</a></td>
</tr>
<tr>
  <td>Slack</td>
  <td><a href="https://mattermost.com/">Mattermost</a></td>
</tr>
<tr>
  <td>Power Apps</td>
  <td><a href="https://budibase.com/">Budibase</a></td>
</tr>
<tr>
  <td>GitHub</td>
  <td><a href="https://gitlab.com/">Gitlab</a></td>
</tr>
<tr>
  <td>IFTTT</td>
  <td><a href="https://github.com/n8n-io/n8n">n8n</a></td>
</tr>
<tr>
  <td>Evernote</td>
  <td><a href="https://docs.paperless-ngx.com/">Paperless-ngx</a></td>
</tr>
<tr>
  <td>Airtable</td>
  <td><a href="https://baserow.io/">Baserow</a></td>
</tr>
<tr>
  <td>Roam Research</td>
  <td><a href="https://opencollective.com/athens">Athens</a></td>
</tr>
<tr>
  <td>Intercom</td>
  <td><a href="https://typebot.io/">Typebot</a></td>
</tr>
<tr>
  <td>Trello</td>
  <td><a href="https://www.focalboard.com/">Focalboard</a>, <a href="https://kan.bn/">kan.bn</a></td>
</tr>
<tr>
  <td>Google Photo</td>
  <td><a href="https://immich.app/">immich</a></td>
</tr>
</tbody>
</table>

<p><strong>Honorable mention</strong>: Grav (CMS) is a great alternative to the already-open-source
WordPress, which have been in a <a href="https://techcrunch.com/2025/01/12/wordpress-vs-wp-engine-drama-explained/">state of drama
lately</a>.
I've been exploring Grav and quite liking it so far.</p>

<p>What's cool with many of those alternatives is that even though they offer a free
self-hosted version that is open-source, they still offer their very own
service to help monetize their project.</p>

<p>It helps getting market adoption, since there's no lock-in because you can
(in most cases I think) always export the data to your self-hosted version when you want to manage your own
instance.</p>
]]></description>
          </item>
                                <item>
          <title>Docker Compose logging to Papertrail</title>
            <link>https://www.juliendesrosiers.com/2020/12/31/docker-compose-logging-papertrail.php</link>
            <pubDate>Thu, 31 Dec 2020 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2020/12/31/docker-compose-logging-papertrail.php</guid>
            <description><![CDATA[<h2>Problems:</h2>

<ol>
<li>There's a limit on how much you can scroll back in your console to see
previous outputs generated by Docker compose.</li>
<li>If you deploy your services on many different servers, you need to chase
logs on all those servers to debug your services.</li>
</ol>

<h2>Solution: Centralized logging</h2>

<ol>
<li><strong>the remote logging service</strong>: papertrail.com</li>
<li><strong>the logging driver</strong>: syslog</li>
</ol>

<h2>Here's how it works</h2>

<p>I use Docker version <code>18.09.7</code>, <a href="https://docs.docker.com/compose/compose-file/compose-versioning/#compatibility-matrix">which means</a> I can use the docker-compose version 3.7 syntax.</p>

<p>In my <code>docker-compose.yml</code> file, under the service I'm logging over to papertrail, I have a <code>logging</code> block, like this:</p>

<pre><code class="yaml">  logging:
    driver: "syslog"
    options:
      syslog-address: "udp://XXXX.papertrailapp.com:XXXX"
      tag: "{{.Name}}/{{.ID}}"
</code></pre>

<p>...Which looks like this, in the context of the service block:</p>

<pre><code class="yaml">  web:
    build: .
    command: bash -c "rm -f tmp/pids/server.pid &amp;&amp; foreman start -f Procfile"
    volumes:
      - ./:/myapp
    ports:
      - "3001:3000"
    depends_on:
      - db
    env_file:
      - .env
    networks:
      - SM
    logging:
      driver: "syslog"
      options:
        syslog-address: "udp://XXXX.papertrailapp.com:XXXX"
        tag: "{{.Name}}/{{.ID}}"
</code></pre>

<h3>BTW, you see that address (<code>XXXX.papertrailapp.com:XXXX</code>)?</h3>

<p>When subscribing to Papertrail, it should give you a specific address just for you, like this (censored):</p>

<p><img src="papertrail-config.png" alt="papertrail url" /></p>

<p>Just copy that address and put it after the <code>udp://</code> part, in your <code>docker-compose.yml</code> config.</p>

<h2>Run it!</h2>

<p>Now, you should be good to go:</p>

<pre><code>user@hostname:~/project-x⟫ docker-compose up
Starting project-x_db_1 ... done
Starting project-x_doc_1 ... done
Starting project-x_web_1 ... done
Starting project-x_ngrok_web_1 ... done
Attaching to project-x_db_1, project-x_doc_1, project-x_web_1, project-x_ngrok_web_1
db_1         |
db_1         | PostgreSQL Database directory appears to contain a database; Skipping initialization
db_1         |
db_1         | 2020-12-31 21:30:48.711 UTC [1] LOG:  starting PostgreSQL [REDACTED] (Debian [REDACTED]) on x86_64-pc-linux-gnu, compiled by gcc (Debian [REDACTED]) [REDACTED], 64-bit
db_1         | 2020-12-31 21:30:48.711 UTC [1] LOG:  listening on IPv4 address "0.0.0.0", port 5432
db_1         | 2020-12-31 21:30:48.711 UTC [1] LOG:  listening on IPv6 address "::", port 5432
db_1         | 2020-12-31 21:30:48.712 UTC [1] LOG:  listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
db_1         | 2020-12-31 21:30:48.727 UTC [25] LOG:  database system was shut down at 2020-12-31 21:22:08 UTC
db_1         | 2020-12-31 21:30:48.733 UTC [1] LOG:  database system is ready to accept connections
doc_1        | [Thu Dec 31 21:30:49.327421 2020] [mpm_prefork:notice] [pid 1] AH00163: Apache/[REDACTED] (Debian) PHP/[REDACTED] configured -- resuming normal operations
doc_1        | [Thu Dec 31 21:30:49.327605 2020] [core:notice] [pid 1] AH00094: Command line: 'apache2 -D FOREGROUND'
web_1        | WARNING: no logs are available with the 'syslog' log driver
ngrok_web_1  | + exec ngrok http '-subdomain=[REDACTED]' '-region=us' -log stdout web:3000
ngrok_web_1  | t=2020-12-31T21:30:49+0000 lvl=info msg="no configuration paths supplied"
[...]
</code></pre>

<h3>See that <code>WARNING</code> after <code>web_1</code>?</h3>

<p>This is "normal". Meaning, it should work anyway. (i.e. it's <em>just</em> a warning)</p>

<p>Now, take a look at your Events<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> page to make sure papertrail is receiving your logs.</p>

<p>You should see something like this:</p>

<p><img src="papertrail-logging.png" alt="papertrail logging interface" /></p>

<h2>Great success!</h2>

<p><img src="greatsuccess.png" alt="great success - Borat" /></p>

<div class="footnotes" role="doc-endnotes">
<hr />
<ol>

<li id="fn:1" role="doc-endnote">
<p><img src="papertrail-config-events-page.png" alt="Events link in your papertrail account" />&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#8617;&#xFE0E;</a></p>
</li>

</ol>
</div>
]]></description>
          </item>
                                <item>
          <title>Getting started with the Monero cryptocurrency</title>
            <link>https://www.juliendesrosiers.com/2020/12/19/get-started-with-monero.php</link>
            <pubDate>Sat, 19 Dec 2020 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2020/12/19/get-started-with-monero.php</guid>
            <description><![CDATA[<p><strong>Warning</strong>: It's likely that if you download the Monero GUI installer (file named like <code>monero-gui-install-win-x64-v0.17.1.6.exe</code>) you will have notices from your antivirus (at least Windows Defender) warning you about the following viruses: <code>PUA:Win32/Vigua.A</code>, <code>Program:Win32/Ymacco.AA29</code> or <code>PUA:Win32/iByteInstaller</code>. <br><strong>This is normal</strong>. Just make sure you remove the installer after installing the Monero GUI. And it should stop nagging you.</p>

<hr />

<p>Here is a hands-on guide on how to make the most out of Monero for trading
without giving up your privacy.</p>

<h2>Concepts</h2>

<p>Monero, like many other cryptocurrencies, has the following things:</p>

<ul>
<li><strong>Miners</strong>: software for you to mine your own coins</li>
<li><strong>Pools</strong>: groups of miners that organize to distribute workload for hashing new
blocks of the block chain. This gives you, the miner (as a person), the
ability to get coins (parts of them) that are mined more frequently than if
you were to mine on your own. Which is long.</li>
<li><strong>Wallets</strong>: You can have as many as you want. A wallet can be created from the
GUI Monero app that you can get from <a href="https://getmonero.com">getmonero.com</a>.

<ul>
<li>It will create a private key (the thing that you keep private and NEVER share to anyone). This key is stored within a .key file, on your computer.</li>
<li>You will need to create a password. It must be strong.</li>
<li>It will give you a mnemonic so you can recover your wallet, should you lose
it (this too, you NEVER share to anyone).</li>
</ul></li>
<li><strong>Currency name</strong>: XMR. (like the BTC of Bitcoin)</li>
<li><strong>Blockchain</strong>: It is based on the Blockchain. So although it's not bitcoin, it's
still based on some of the same basic principles of many (or all) other
cryptocurrencies.</li>
</ul>

<h2>1. Getting a wallet</h2>

<ol>
<li>(On Windows) Temporarily <strong>disable your Virus Real-time protection</strong>, because Monero is
recognized by antiviruses for being malware, since it's often installed on
machines to mine coins for hackers who have taken control of machines. But
in this case, everything is under your control, so it's safe to disable it.
It will be reactivated in a few hours.</li>
<li><strong>Download the <a href="https://www.getmonero.org/downloads/#gui">GUI wallet</a></strong> for your platform: <a href="https://www.getmonero.org/downloads/#gui">www.getmonero.org/downloads</a></li>
<li><strong>Install the GUI Wallet app</strong> with all the default options:<br>
<ul class="set-of-screenshots">
  <li><img src="images/monero-installation-wizard-1.png" alt="Monero GUI Installation Wizard Step 1"></li>
  <li><img src="images/monero-installation-wizard-2.png" alt="Monero GUI Installation Wizard Step 2"></li>
  <li><img src="images/monero-installation-wizard-3.png" alt="Monero GUI Installation Wizard Step 3"></li>
  <li><img src="images/monero-installation-wizard-4.png" alt="Monero GUI Installation Wizard Step 4"></li>
  <li><img src="images/monero-installation-wizard-5.png" alt="Monero GUI Installation Wizard Step 5"></li>
  <li><img src="images/monero-installation-wizard-6.png" alt="Monero GUI Installation Wizard Step 6"></li>
</ul></li>
<li><strong>Setup your Wallet</strong>:<br>
<ul class="set-of-screenshots">
  <li><img src="images/monero-gui-app-setup-1.png" alt="Monero GUI app setup 1"></li>
  <li><img src="images/monero-gui-app-setup-2.png" alt="Monero GUI app setup 2"></li>
  <li><img src="images/monero-gui-app-setup-3.png" alt="Monero GUI app setup 3"></li>
  <li><img src="images/monero-gui-app-setup-4.png" alt="Monero GUI app setup 4"></li>
  <li><img src="images/monero-gui-app-setup-5.png" alt="Monero GUI app setup 5"></li>
  <li><img src="images/monero-gui-app-setup-6.png" alt="Monero GUI app setup 6"></li>
  <li><img src="images/monero-gui-app-setup-7.png" alt="Monero GUI app setup 7"></li>
</ul></li>
<li>(On Windows) you might want to <strong>exclude the following folders</strong> from being scanned by your antivirus:

<ul>
<li><strong>Control Panel</strong> > <strong>System and Security</strong> > <strong>Security and Maintenance</strong> > <strong>Security</strong> > <strong>Virus Protection</strong> > <strong>View in Windows Security</strong> > <strong>Virus &amp; threat protection settings</strong> > <strong>Manage settings</strong> > <strong>Exclusions</strong> > <strong>Add or remove exclusions</strong></li>
<li>Add the following exclusions:

<ul>
<li><code>C:\Program Files\Monero GUI Wallet</code></li>
<li><code>C:\Users\[YOUR USERNAME]\OneDrive\Documents\Monero</code><br>
(<code>OneDrive</code> might not be there if you're not backuping your documents with OneDrive)</li>
</ul></li>
</ul></li>
</ol>

<h2>2. Purchasing XMR coins</h2>

<p>The easiest way (to my knowledge) to purchase XMR coins is to use whatever
online wallet you have like coinbase or others, to first purchase your bitcoins, with your normal credit card, 
then through an exchange such as Poloniex, trade your BTC (bitcoin) to XMR (monero),
and then send your XMR to your local wallet (installed in the previous step).</p>

<p>Here is a short video on how to exchange BTC to XMR:</p>

<p><a href="exchanging-btc-for-monero.m4v"><img src="images/monero-exchange-video-preview.png" alt="Monero Exchange with Poloniex" /></a></p>

<p>This gives you 2 advantages:</p>

<ol>
<li><strong>Ease of use</strong>: easy to buy crypto coins (no need for <a href="https://localbitcoins.com/">localbitcoins.com</a> or things like those)</li>
<li><strong>Privacy</strong>: still have a high level of privacy because your bitcoins will become XMR
(monero) afterwards, which is pretty much untraceable (YAY!).</li>
</ol>

<h2>3. Buying things</h2>

<p>Basically, you can but things in the same manner as you would with Bitcoins:</p>

<ol>
<li>you need the destination XMR wallet address.</li>
<li>you write that address in your wallet, in the 'send' tool</li>
<li>you put a bit more money (see <a href="https://www.monero.how/monero-transaction-fees">this page</a>), just so the transaction doesn't take too much time to be processed.</li>
<li>(optional) choose a ring of at least 6 verifications</li>
<li>confirm, and wait for the verifications to be done, after which the
transaction will be considered to be done. It usually takes about 15-20
minutes.</li>
</ol>

<h3>A note about Bitcoins</h3>

<p>There are places that accept XMR payments, but there are a lot more places that
accept Bitcoin, since it's more popular. In those cases, you can use
<a href="https://xmr.to/">xmr.to</a>.</p>

<h2>4. Mining</h2>

<p>If you want to mine, you need at least the following:</p>

<ul>
<li>a mining software (a "miner")</li>
<li>an internet connexion</li>
<li>a CPU, or a GPU (in your graphics card)</li>
<li>time</li>
<li>power (kWh)</li>
</ul>

<p>And <em>most</em> of the time, you will want to join a pool, so you don't wait super
long for one XMR to be attributed to you. A pool will make it shorter for you
to get paid <em>some</em> amount (calculated according to your contribution) for the mining that
you provided.</p>

<p>When you use a pool, the mining software you'll use will depend on which one is
supported by the pool you join.</p>

<p>But for example, if you join <a href="https://minexmr.com/">minexmr</a>, which I tried,
you will have to use <a href="https://github.com/xmrig/xmrig/releases">XMrig</a>. But see
their <a href="https://minexmr.com/#getting_started">info page</a> for more info.</p>

<h3>Antivirus Note</h3>

<p>If you choose to mine XMR, you <em>will</em> have to exclude the location
of the miner that you install, in your antivirus settings. Because otherwise,
it will be flagged as a virus (yes, some people hack into other computers just so
they can use their computer power for mining Monero. That's why).</p>

<h2>Acknowledgements</h2>

<p>Thanks to my friend Simon Proulx, who introduced me to this technology, and helped me
grok some of the concepts and practices explained in this article.</p>
]]></description>
          </item>
                                              <item>
          <title>The Fman File Browser</title>
            <link>https://www.juliendesrosiers.com/2020/11/19/fman-file-browser.php</link>
            <pubDate>Thu, 19 Nov 2020 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2020/11/19/fman-file-browser.php</guid>
            <description><![CDATA[<p><a href="https://fman.io/">fman</a> is a Dual-pane file manager for Mac, Windows and Linux.</p>

<p>I decided to try it for my Windows (10) machine. Here are my thoughts.</p>

<h2>Things I don't like about fman:</h2>

<h3>1. Context menu is radically stripped down</h3>

<p>This is huge for me. Like, I have no:</p>

<ul>
<li>7-Zip option to extract zip the way I prefer ("Extract Here").</li>
<li>Share options (Dropbox, OneDrive, etc)</li>
<li>Git Bash Here (to open the current folder in the terminal)</li>
<li>Open with Code (to open the current folder in VS Code)</li>
<li>New [file], to create a file in the current folder (this is very handy to
create new text files where I'm in). And directly open them in a text editor
of my choice.</li>
</ul>

<h3>2. No way to preview image files</h3>

<p>I often need to see which image I need in a folder full of images. At least in
Windows File Explorer I can have a Large Icons view in which I can have a hint
of what's inside. I'm used to be able to do this very easily on the mac
(Finder) by just hitting spacebar, to Preview the file without actually opening
an app to fully load it.</p>

<p>Previewing is not just for images, but PDFs too. It's very easy to be lost
when all you see is a list of file names.</p>

<h3>3. No way to see which volumes are mounted and available</h3>

<p>This is too bad. I like to access my NAS or other mounted (encrypted) volumes,
without remembering which Letter it was assigned by Windows.</p>

<h3>4. No handling of High Contrast mode</h3>

<p>For people with visual difficulties, it is nice to have an interface that use
more contrast. It's not a big downer for me, but I find it lacks consistency on
my setup since that's what I'm using. I quite like the minimalist &amp; brutalist
feel of <a href="https://support.microsoft.com/en-us/windows/use-high-contrast-mode-in-windows-10-fedc744c-90ac-69df-aed5-c8a90125e696">Windows' High Contrast
mode</a>. Plus, it's easier for my old eyes. ;-)</p>

<h2>Things I do like about fman:</h2>

<h3>1. You can easily tidy up and classify</h3>

<p>This is the big win that fman gives: a quick and easy way to have a split File
explorer with which you can move around files from one remote place to another.</p>

<h3>2. Very useful fuzzy search to find files and folders</h3>

<p>This makes it a breeze to go from one place to another without moving your
hands off the keyboard (I love to use keyboard shortcuts for this reason).</p>

<h3>3. More intuitive keys for navigating folders</h3>

<p>Arrow keys are a much better way to navigate between folders than the File
Explorer keyboard shortcuts that are a very awkward in comparison.</p>

<h2>Conclusion</h2>

<p>I don't regret buying it, but I will use the default File Explorer almost exclusively from
now on... until a best alternative comes up for Windows.</p>
]]></description>
          </item>
                                <item>
          <title>Setup WireGuard on Ubuntu and Windows</title>
            <link>https://www.juliendesrosiers.com/2020/11/13/setup-wireguard-ubuntu.php</link>
            <pubDate>Fri, 13 Nov 2020 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2020/11/13/setup-wireguard-ubuntu.php</guid>
            <description><![CDATA[<p><a href="https://www.wireguard.com/">WireGuard</a> is a modern VPN. Arguably the best in many crucial aspects like security, and simplicity, at the moment of writing this.</p>

<p>So here's how to setup WireGuard on Ubuntu 20.04, and also, how to setup the corresponding WireGuard client on a Windows 10 machine.</p>

<h2>Concepts</h2>

<h3>Keys</h3>

<p>We'll use <strong>Keys</strong> (key pairs): Public and Private keys.</p>

<h3>IP addresses</h3>

<p>We'll see <strong>IP Addresses</strong>: Those of the VPN server, the computer where it's
installed, and the DNS server used by the client. We'll use 35.174.118.17 in
this tutorial, but please get the one that your server is using. To find it,
simply do an <code>ifconfig</code> on your server, and look for <code>inet</code>, in the output.</p>

<h3>Ports</h3>

<p>We'll use a <strong>port</strong>, which is a kind of door to enter into a system. That door
can either be open, or with a firewall, it can be "closed". We'll use the port <code>61951</code> throughout this tutorial, but it could be something else like <code>51820</code>, or <code>54321</code>. As long as it isn't used by another service on the server.</p>

<h3>(network) Interfaces</h3>

<p>A network <strong>interface</strong> is just a short name for a network entry, for a computer to communicate in a network.
Interfaces have names that look like <code>eth0</code>, <code>wlan</code>, and such. You
can see what I mean by doing an <code>ifconfig</code> on a Linux or macOS shell. It's the first part before the column <code>:</code>. For example:</p>

<pre><code>ens3: flags=4163&lt;UP,BROADCAST,RUNNING,MULTICAST&gt;  mtu 1500
        inet 35.174.118.17  netmask 255.255.254.0  broadcast 35.174.118.255
        inet6 2001:19f0:4:2360:5402:3ff:fe0f:c619  prefixlen 64  scopeid 0x0&lt;global&gt;
        inet6 fe90::5402:3ff:fe0f:c619  prefixlen 64  scopeid 0x20&lt;link&gt;
        ether 56:00:03:0f:b6:19  txqueuelen 1000  (Ethernet)
        RX packets 1070983  bytes 786173353 (786.1 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 1053635  bytes 803986773 (803.9 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
</code></pre>

<p>See that <code>ens3</code>? that's an interface name.</p>

<p>On windows, <code>ipconfig</code> is the equivalent command, but it's not showing that interface name. But it doesn't matter for
us, as it is only the network interface of the server that we'll need.</p>

<h2>On the (ubuntu) server</h2>

<h3>Pre-requisites for the server</h3>

<ul>
<li>Ubuntu (20.04, which is an LTS) installed on that machine</li>
<li>Root access to that machine</li>
</ul>

<h3>Before we do anything</h3>

<p>Take note of your current IP address, by going at <a href="https://www.whatismyip.com/">whatismyip.com</a> in your browser.</p>

<h3>Installation steps</h3>

<pre><code>sudo apt update
sudo apt install wireguard
</code></pre>

<p>Then, we configure the firewall (<code>ufw</code>) to allow traffic to go through the
port <code>61951</code>:</p>

<pre><code>sudo ufw allow 61951/udp
</code></pre>

<p>The <code>udp</code> part is just another way of transmitting packets. Normally we use
<code>tcp</code>, but in the case of VPNs, we usually want faster transfer, so in this
case we use <a href="https://fr.wikipedia.org/wiki/User_Datagram_Protocol">UDP</a>.</p>

<h4>Configuration</h4>

<blockquote>
  <h5>Note on the text Editor</h5>
  
  <p>You'll need to edit the <code>/etc/sysctl.conf</code> file, so you will need <code>sudo</code> and a text editor like
  <code>nano</code> or <code>vim</code>. If you don't know <code>vim</code>, I suggest <code>nano</code>. In this tutorial I will use <code>nano</code>.</p>
</blockquote>

<pre><code>sudo nano /etc/sysctl.conf
</code></pre>

<p>and then, remove the comment mark (<code>#</code>) before the <code>net.ipv4.ip_forward=1</code>
line.</p>

<p>Save the file, and exit the editor.</p>

<p>To apply the changes, do:</p>

<pre><code>sudo sysctl -p
</code></pre>

<p>Now, we'll generate the <strong>keys</strong> for the server:</p>

<pre><code>wg genkey | sudo tee /etc/wireguard/server_private.key | wg pubkey | sudo tee /etc/wireguard/server_public.key
</code></pre>

<p>Now, if you (sudo) <code>ls</code> inside <code>/etc/wireguard</code>, you should see those 2 files
created.
And since the private key is, well... private, let's secure it with this
command:</p>

<pre><code>sudo chmod 600 /etc/wireguard/server_private.key
</code></pre>

<p>To see the keys you just generated, do:</p>

<pre><code>sudo cat /etc/wireguard/server_private.key
cat /etc/wireguard/server_public.key
</code></pre>

<p>Now, we'll create a file for wireguard configuration:</p>

<pre><code>sudo nano /etc/wireguard/wg0.conf
</code></pre>

<p>And put that, for now (more to come later):</p>

<pre><code>[Interface]
PrivateKey = oCH7Z0g+ieQ99KkkR1E5EO22Evs5q75F+ES4O4Oc93E= # The server_private.key value.
Address = 10.5.5.3/24 # Internal IP address of the VPN server.
ListenPort = 61951 # Previously, we opened this port to listen for incoming connections in the firewall (ufw)
# Change "ens3" to the name of your network interface in the following two settings. This commands configures iptables for WireGuard.
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o ens3 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o ens3 -j MASQUERADE
</code></pre>

<p>You will need to change those values:</p>

<ul>
<li>PrivateKey value (<code>oCH7Z0g[...]</code>) to the one you'll find in <code>server_private.key</code>.</li>
<li>The Address value, which you can find in the private network settings of your
server dashboard. For example, on <a href="https://www.vultr.com/?ref=7477257">vultr</a>, it would be in your instance's
<strong>Settings</strong> tab, under the <strong>Private Network</strong> section. It's an address that
look like this: <code>10.5.5.3</code>. You'll also add <code>/24</code> at the end of it.</li>
<li>The ListenPort value (<code>61951</code>) is the same we talked about at the beginning.
It's the port through which the VPN server will communicate with your client.</li>
<li>In PostUp, and PostDown, you'll see <code>ens3</code>. Change that to the
<strong>interface</strong> that you get when you do <code>ip -o -4 route show to default | awk '{print $5}'</code> 
on your server.
For example, it could be <code>ens3</code>, so change that as needed.</li>
</ul>

<p>You can now save and exit this file. And set the permissions on that file more tightly:</p>

<pre><code>sudo chmod 600 /etc/wireguard/wg0.conf
</code></pre>

<p>And now, do this to start WireGuard VPN:</p>

<pre><code>sudo systemctl start wg-quick@wg0
</code></pre>

<p>And run this almost identical command, to make the VPN server always start after a
reboot of your server:</p>

<pre><code>sudo systemctl enable wg-quick@wg0
</code></pre>

<h3>the <code>wg0</code> interface</h3>

<p>Now, bring <code>up</code> the <code>wg0</code> interface with this command:</p>

<pre><code>sudo wg-quick up wg0
</code></pre>

<p>So now, if you issue <code>ifconfig</code>, you will see <code>wg0</code> among the interfaces. Something like this:</p>

<pre><code>wg0: flags=209&lt;UP,POINTOPOINT,RUNNING,NOARP&gt;  mtu 1420
        inet 10.1.96.0  netmask 255.255.240.0  destination 10.1.96.0
        unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  txqueuelen 1000  (UNSPEC)
        RX packets 443061  bytes 88601148 (88.6 MB)
        RX errors 0  dropped 16144  overruns 0  frame 0
        TX packets 671392  bytes 739971080 (739.9 MB)
        TX errors 0  dropped 233 overruns 0  carrier 0  collisions 0
</code></pre>

<p>Note that it is no longer <code>10.5.5.3</code> but it has inet <code>10.1.96.0</code>. It's normal.
It also changed the <code>wg0.conf</code> to reflect that.</p>

<p>If you want to disable that interface, simply do <code>down</code> instead.</p>

<p>But if you want to change the configuration, you can do this after the change:</p>

<pre><code>sudo systemctl restart wg-quick@wg0
</code></pre>

<h2>On the client side (Windows)</h2>

<ol>
<li>Download the installer from
<a href="https://www.wireguard.com/install/">https://www.wireguard.com/install/</a></li>
<li>Execute the <code>.msi</code> file that you'll get. And now you should see a WireGuard
logo in your system tray.</li>
</ol>

<h3>Configuration</h3>

<p>In the WireGuard Windows UI, go to the bottom <em>Add Tunnel</em> button, and select
<em>Add Empty Tunnel</em>:
<img src="vpn-win-config-1.png" alt="Add Tunnel > Add Empty Tunnel" /></p>

<p>Next, you'll see this window, but with a different value for the <strong>Private
Key</strong>:</p>

<p><img src="vpn-win-config-2.png" alt="&quot;Create new tunnel&quot; window" /></p>

<p>This is your base configuration. You'll need to add a few lines here:</p>

<p>Address, and DNS. Like this:</p>

<pre><code>[Interface]
PrivateKey = OCJx8Im1MyHvNRsrBR7bWWWmYUbDOQ5W/YQg2vA6KWA=
Address = 192.168.50.223/24 # IP address of the client's wg0 interface.
DNS = 1.1.1.1
</code></pre>

<p>The <strong>Address</strong> field is the IP that the VPN client's network interface will occupy. It must not be already in use. A good candidate for this IP can be the value of your main network interface
(found in <code>ipconfig</code> in your windows terminal), and you add 1 to the last
digit, just to make it different (i.e. not used). For example, I have this for my wifi
interface I'm using on my windows machine:</p>

<pre><code>Wireless LAN adapter Wi-Fi:

   Connection-specific DNS Suffix  . :
   Link-local IPv6 Address . . . . . : fe80::ac40:f1f7:e086:9257%10
   IPv4 Address. . . . . . . . . . . : 192.168.50.222
   Subnet Mask . . . . . . . . . . . : 255.255.255.0
   Default Gateway . . . . . . . . . : 192.168.50.1
</code></pre>

<p>So I took <code>192.168.50.223</code> (added 1 to 222), so the Address I will put on my
configuration will be <code>192.168.50.223/24</code>.</p>

<p>The <strong>DNS</strong> can be a public DNS IP that you can use for free. I suggest you take
something like <code>1.1.1.1</code> (graciously offered by Cloudflare) or <code>9.9.9.9</code>
(graciously offered by <a href="https://www.quad9.net/">Quad9</a>.</p>

<p>Now, after the <code>[Interface]</code> block, add another. The <code>[Peer]</code> configuration
block:</p>

<pre><code>[Peer]
PublicKey = uZik78EWgYCLQRMdG6k6QK0mzHFqfr4uhOEjPyXe5WE=
AllowedIPs = 0.0.0.0/0
Endpoint = 35.174.118.17:61951
</code></pre>

<p><strong>PublicKey</strong> is what you can find on your server, in <code>/etc/wireguard/server_public.key</code>.</p>

<p><strong>AllowedIPs</strong> can stay 0.0.0.0/0.</p>

<p><strong>Endpoint</strong> is the public IP of your server, followed by the <strong>port</strong> number.</p>

<h3>Recap of the client config file</h3>

<p>Your client config on windows should now look like this:</p>

<pre><code>[Interface]
PrivateKey = OCJx8Im1MyHvNRsrBR7bWWWmYUbDOQ5W/YQg2vA6KWA=
Address = 192.168.50.223/24 # IP address of the client's wg0 interface.
DNS = 1.1.1.1

[Peer]
PublicKey = uZik78EWgYCLQRMdG6k6QK0mzHFqfr4uhOEjPyXe5WE=
AllowedIPs = 0.0.0.0/0
Endpoint = 35.174.118.17:61951
</code></pre>

<p>Now make sure you give it an appropriate name so you remember what is this
WireGuard instance (you can have many), and click <strong>Save</strong>. 
You should now see the new Tunnel's name in Tunnels tab.</p>

<p>Click <strong>Activate</strong>, and if you see a green shield icon with Active, next to the
Status line, it means it worked. Now try to browse the web in your browser. If
it doesn't work, try rebooting your machine, or reviewing the previous steps.</p>

<p>You can now go to <a href="https://www.whatismyip.com/">whatismyip.com</a> and you should
see the VPN server's IP address instead of your own. Hurray!</p>

<h3>Go back to the server</h3>

<p>Once you're done with the client configuration, go back to the server
configuration, and add a <code>[Peer]</code> block, after the <code>[Interface]</code> block, in <code>/etc/wireguard/wg0.conf</code>, so that it looks like this:</p>

<pre><code>[Interface]
PrivateKey = oCH7Z0g+ieQ99KkkR1E5EO22Evs5q75F+ES4O4Oc93E= # The server_private.key value.
Address = 10.5.5.3/24 # Internal IP address of the VPN server.
ListenPort = 61951 # Previously, we opened this port to listen for incoming connections in the firewall (ufw)
# Change "ens3" to the name of your network interface in the following two settings. This commands configures iptables for WireGuard.
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o ens3 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o ens3 -j MASQUERADE

[Peer]
PublicKey = AXZBCUqrlAGnt2jddHNu6ECQ0yp8mbQxPzefP6j8VCo=
AllowedIPs = 192.168.50.223/32
</code></pre>

<p>In this new <code>[Peer]</code> configuration block, we have this:</p>

<p><strong>PublicKey</strong>, which is the Public key that was generated on your windows
machine at the same time that you created your "Tunnel".</p>

<p><strong>AllowedIPs</strong> is the IP of the interface that will be created by WireGuard on
your local machine (the client). See above.</p>

<p><strong>Endpoint</strong> is the real public IP address of your computer when it's connected to the
web.</p>

<p>Now restart the server:</p>

<pre><code>sudo systemctl restart wg-quick@wg0
</code></pre>

<p>And if you go to <a href="https://www.whatismyip.com/">whatismyip.com</a> in your browser,
you should see your server's IP address. If so, it worked.</p>

<p>Otherwise, feel free to send me an email or a question on twitter.</p>

<h2>Acknowledgements</h2>

<p>I copy/pasted a lot of steps from
<a href="https://serverspace.us/support/help/set-up-wireguard-vpn-server-on-ubuntu/">that article from serverspace.us</a>, for the server configuration steps,
so here's some back-link for their SEO juice. ;-)</p>

<p>I also used
<a href="https://golb.hplar.ch/2019/07/wireguard-windows.html">that article</a> for the
Windows (client) configuration part.</p>

<p>Special thanks to <a href="https://pascalmeunier.info/">Pascal Meunier</a>, founder of <a href="https://www.hivetek.com/">Hivetek</a> who told me about
WireGuard, and who was kind enough to answer my questions on the subject.</p>
]]></description>
          </item>
                                <item>
          <title>The Tools I use</title>
            <link>https://www.juliendesrosiers.com/2020/09/19/tools-i-use.php</link>
            <pubDate>Sat, 19 Sep 2020 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2020/09/19/tools-i-use.php</guid>
            <description><![CDATA[<p><strong>Update</strong>: check out my <a href="https://www.juliendesrosiers.com/2021/08/21/tools-for-thinking.php">new post</a> on the tools I use for thinking, specifically.</p>

<hr />

<p>I use a few tools in my day to day work as a Web developer. Here they are, in
case you need some inspiration or ideas:</p>

<h2>The OS</h2>

<p>I use Windows. 10. Pro.</p>

<p>But I used to use macOS. I was using Macs since 2007, when I think the version was Mac
OS X Tiger.
It was the perfect thing. Actually, I think the perfect thing came with Snow
Leopard. From then, it just kept moving in a direction I didn't like. With
<a href="https://en.wikipedia.org/wiki/Skeuomorph">skeuomorphisms</a>, among other things.</p>

<p>But since 2016 I am excited about deep learning (for CNN, more specifically) so I built a machine just
for that, with Windows as the main OS. But I never actually used the CUDAs in my
GeForce. Yet.</p>

<h2>The software</h2>

<h3>Text editor</h3>

<p>I'm a bit odd. I really have a thing for Vim, but my vim settings kindof stalled since about 2015. Now, I'm using Pathogen to manage my plugins, in a vimrc that comes with a dotfiles git repo I clone and initialize on all the machines I work on.</p>

<p>So Vim it is, for most of my code. Not GnuVim the UI app. Vim through an SSH
session on the servers I work on. (Told you I'm odd!)</p>

<p>This is why I didn't customized much of my vim environment, except for syntax
highlighting the programming languages I work on.</p>

<h3>The <em>programming</em> languages</h3>

<h4>For Frontend stuff</h4>

<ul>
<li>JavaScript/ES6. With no preprocessor. Except for when I worked on React
projects, but it's less the case now since I've gone back to my comfortable
habit of using jQuery and keeping it simple on the frontend.</li>
<li>TypeScript, for a client project. I inherited that codebase. It's not bad.
But it's certainly not a good idea for the use case I'm working on, which is
a <em>website</em> (i.e. not a web <em>app</em>).</li>
</ul>

<h4>For backend stuff</h4>

<ul>
<li>PHP</li>
<li>Ruby (with a lot of <a href="https://rubyonrails.org/">Rails</a>)</li>
<li>Go</li>
<li>Node.js</li>
</ul>

<p>(in that order)</p>

<h2>Databases</h2>

<ul>
<li>When I can choose, it's PostgreSQL.</li>
<li>When I <em>cannot</em> choose, it doesn't matter most of the time, because WordPress
only works on MySQL-like DBs.</li>
</ul>

<h2>Devops</h2>

<ul>
<li>Ubuntu or Debian distributions for the servers I develop on and deploy to.
Developing through SSH on remote servers means 2 things:

<ol>
<li>when I'm on the go, I can use a dumb netbook and connect through my
blackberry-enabled wifi, fire a terminal and not worry about any kind of
setup.</li>
<li>It's always the same computer. With a real Linux installation (which in
many respects is better than WSL).</li>
</ol></li>
<li>Docker + Docker Compose. I haven't checked on Kubernetes yet, but something
is telling me I don't need that kind of heavy tooling just yet. So it's
pretty simple.</li>
<li>Heroku when possible, for the hosting. I know it's expensive, but so is my
time.</li>
<li>AWS can come handy for things like S3, lambda, and other things that one
would need for (often prematurely) scaling.</li>
</ul>

<h2>GUI apps</h2>

<ul>
<li><a href="https://fman.io/">fman</a> as a File Explorer alternative</li>
<li><a href="https://www.foobar2000.org/">foobar2000</a> for playing my MP3s (no, I don't have a subscription to a music
service. I prefer buying my mysic whenever possible)</li>
<li>"git bash" for the Terminal</li>
<li>Chrome, with Lastpass for managing passwords</li>
<li>Affinity {Designer,Photo,Publisher}, by <a href="https://affinity.serif.com/">Serif</a>, for graphic stuff. This is an awesome and
cheap alternative to Adobe's dominance in that field.</li>
<li>Office 365 subscription for Excel, Word and other things. I'm not so proud
about that one, but it works well on Windows. And OneDrive comes with 1TB of storage, which is nice.</li>
<li><a href="https://kdenlive.org/">Kdenlive</a> for video editing.</li>
<li><a href="https://obsproject.com/">OBS Studio</a> for video capturing.</li>
</ul>

<h2>Web apps</h2>

<ul>
<li><a href="https://kinopio.club/">Kinopio.club</a> for linking ideas together.</li>
<li><a href="https://pinboard.in/u:jdesrosiers">Pinboard.in</a> for storing and managing my
bookmarks.</li>
<li><a href="https://airtable.com/">Airtable</a> for playing with data and creating useful
things with them.</li>
<li><a href="https://todoist.com/">Todoist</a> for keeping track of my todos.</li>
<li><a href="https://www.brain.fm/">Brain.fm</a> as my surefire way to get in the zone.</li>
</ul>

<h2>Misc</h2>

<ul>
<li><a href="https://www.tclcom.com/bb/support/keyone/">Blackberry KeyOne</a>. It's pretty
good. I like the physical keyboard that only Blackberry seems to be doing
these days. Again, I come from an Apple world, so I was used to having
iPhones, but when I no longer needed to work on Macs, I gave it another
thought. And the fact that it is on Android makes it just like any other
phone in terms of capabilities.</li>
</ul>

<h2>A word about leaving Google</h2>

<p>Like many people, I am concerned about the extent of Google's knowledge of my
personal life, so I've taken steps to counter that:</p>

<ul>
<li>I use DuckDuckGo as my default search engine, even if quite a few times I
need to prepend my searches with <code>!g</code> to get better results.</li>
<li>I am using a mix of <a href="https://proton.me/mail">Proton Mail</a>, Fastmail and Zoho Mail for more and more of
my needs and subscriptions. But I've yet to completely get rid of my gmail.
And it probably won't ever happen for many reasons.</li>
<li>I am using Amazon Photo on my phone, instead of Google Photos for storing
photos I take, to the cloud. Having a Prime subscription helps with that.</li>
<li>I began using F-Droid as an alternative to Google Play store.</li>
</ul>

<p>Things I still am looking for:</p>

<ul>
<li>A good alternative to Google Music for purchasing my music.</li>
</ul>

<p>I am aware that I'm using solutions from big, and arguably equally
monopolistic players as alternatives, but I think it's more practical this way,
and there is still value in diversifying.</p>

<h2>Postface</h2>

<p>I wrote this blog post in Vim, through an SSH session on the
<a href="https://www.dreamhost.com/r.cgi?2162294">Dreamhost</a> server that is hosting this website. My "blogging engine" uses Markdown.</p>
]]></description>
          </item>
                                <item>
          <title>A Python trick that helps developping in Ruby on Rails</title>
            <link>https://www.juliendesrosiers.com/2020/08/31/python-trick-ruby-on-rails.php</link>
            <pubDate>Mon, 31 Aug 2020 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2020/08/31/python-trick-ruby-on-rails.php</guid>
            <description><![CDATA[<p>Back in the days, when I was learning Python and did a few
<a href="https://github.com/juliend2/PYP">projects</a> with it, I realized that I could
run Python code directly by running a class' file, without introducing any side
effect to the rest of the code that was using that class.</p>

<h3>Here is the example</h3>

<pre><code>def main():
    foo = Base1()
    foo.base1()

if __name__ == "__main__":
    main()
</code></pre>

<p>Source:
<a href="https://stackoverflow.com/a/20532554/242404">https://stackoverflow.com/a/20532554/242404</a></p>

<h4>Explanation</h4>

<p>There is a <a href="https://www.freecodecamp.org/news/whats-in-a-python-s-name-506262fe61e8/">very nice article</a> that delves into the details, but in short, <code>__name__</code> is a special Python variable that contains the name of the currently running script. When the script in question is the current file, <code>__name__</code> will evaluate to <code>"__main__"</code>.</p>

<p>(Notice how Python loves <code>__</code>s. It's almost as if those names were snakes,
creeping into your code.)</p>

<h3>How I use it in Ruby (on Rails)</h3>

<pre><code>class Page &lt; ApplicationRecord
  # some ruby code for my model
end

if __FILE__ == $0
  p = Page.find(1)
  p.testing_something
end
</code></pre>

<p>And in your terminal, all you have to do to run what's inside that last <code>if</code>
statement, is to do:</p>

<pre><code>rails runner app/models/page.rb
</code></pre>

<p><strong>Boom</strong>. No need for a spec, no need for a rake task. Just one quick and dirty condition, right inside the context of your class.
To me, this is quite nice, for those cases when I'm too lazy to take the time
to write a full blown test case, but just see if something works, or not.</p>
]]></description>
          </item>
                                <item>
          <title>Notes on trying Linux Desktop in 2020</title>
            <link>https://www.juliendesrosiers.com/2020/07/25/notes-on-using-linux.php</link>
            <pubDate>Sat, 25 Jul 2020 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2020/07/25/notes-on-using-linux.php</guid>
            <description><![CDATA[<h3>Earlier this year: Ubuntu 20</h3>

<p>Here's how it went:</p>

<ol>
<li>2020-05-23: Chrome, while using Notion.so, crashed.</li>
<li>2020-05-25: Firefox, while using Slack, crashed.</li>
</ol>

<p>And when I say "crashed", I mean: no response. Even if I would wait for 1 hour.
It froze. Hard.</p>

<p>The first freezing happened while I was using the version 440 of the NVIDIA driver
for my video card.
The second freezing happened while I was using the default one (can't remember but
it was not far from the 440, but it was the default while installing the
system).</p>

<p>The third problem that manifested itself a few days later has to do with the fact that I encrypted my home folder.
There was the following error while booting:</p>

<pre><code>cryptsetup: waiting for encrypted source device /swapfile
[...]
Initramfs unpacking failed.
Decoding failed.
</code></pre>

<p>That ended for me that latest experiment with Ubuntu.</p>

<p>Which us me to my...</p>

<h3>Latest experiment (July 2020): Debian with KDE</h3>

<p>I think I'm having more success now.</p>

<p>I installed Debian 10, the one that comes with KDE.</p>

<p>Some things I had to do that helped me:</p>

<h4>1. Keyboard shortcut to display the Desktop</h4>

<p>I use <code>CTRL-ALT-D</code>. <a href="https://forum.kde.org/viewtopic.php?f=66&amp;t=53190">This post</a> showed me the way to do so.</p>

<h4>2. Make Joplin work</h4>

<p>The normal installation didn't work on my setup. According to <a href="https://discourse.joplinapp.org/t/joplin-under-debian-10/8028">This thread</a> Debian 10 changed something
   recently that brought some compatibility issue with apps like Joplin.</p>

<p>To get around this, I had to modify the desktop shortcut I use for starting
   Joplin, and modified the command to add the <code>--no-sandbox</code> option at the
   end.</p>

<h4>3. Redshift: to avoid blue light in the night (helps to sleep faster)</h4>

<p>It's like <a href="https://justgetflux.com/">f.lux</a>, but Open source and works on linux. The funny thing is: I just realized while writing this post that f.lux was actually compatible with Linux.</p>

<p>So anyway, I grabbed <a href="https://www.addictivetips.com/ubuntu-linux-tips/filter-blue-light-on-linux-redshift/">Redshift</a> and it does the job.
   And no need to pass longitude &amp; latitude when starting it. It figures out by
   itself (according to my experience, at least).</p>

<h4>4. (Only if dual booting with Windows) Avoid breaking the time on Windows</h4>

<p>For some reasons, Windows and Linux don't manage time the same way. So unless
  you're lucky enought to live in the GMT time zone, you might want to take a
  look at <a href="https://www.youtube.com/watch?v=IphgjkLAY7Q">that video</a> that shows what changes you have to do in your system's registry (using Regedit.exe).</p>

<h2>Conclusions</h2>

<p>So far I'm happy.</p>

<p>I don't boot on Debian very often, but at least it's there. And it never
crashed on my like Ubuntu did. Maybe it's due to the different desktop
environment that uses my video card differently. I don't know.</p>

<p>But I figured I would try this more conservative approach, with a less bleeding
edge distro, that is reliable and stable.</p>

<p>So far, I got Zoom working out of the box with my Logitech webcam (no driver
installation needed), and my laser printer worked out of the box as well.
That's pretty good already.</p>

<p>So far so good.</p>
]]></description>
          </item>
                                <item>
          <title>Backing up your Thunderbird profile</title>
            <link>https://www.juliendesrosiers.com/2020/06/26/backup-thunderbird-profile.php</link>
            <pubDate>Fri, 26 Jun 2020 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2020/06/26/backup-thunderbird-profile.php</guid>
            <description><![CDATA[<h2>First thing: what is a Thunderbird Profile ?</h2>

<p><strong>(Note: I am NOT responsible for the loss of your data. These instructions are
only there to help you learn things.)</strong></p>

<blockquote>
  <p>In <a href="https://www.thunderbird.net/">Thunderbird</a>, the profile stores two main sets of items. First, it stores your local mail, and possibly copies of messages that reside on the mail server (depending on your account configuration). Second, it stores any changes you make while using Thunderbird (for example, changes to account settings and changes to the toolbar).</p>
</blockquote>

<p>&mdash; <a href="https://support.mozilla.org/en-US/kb/profiles-where-thunderbird-stores-user-data?redirectlocale=en-US&amp;as=s&amp;s=profiles&amp;r=1&amp;esab=a&amp;redirectslug=profiles-tb#w_what-is-a-profile">Source</a>.</p>

<h2>Finding your Profiles folder</h2>

<p>If you need help with that, <a href="https://support.mozilla.org/en-US/kb/profiles-where-thunderbird-stores-user-data?redirectlocale=en-US&amp;as=s&amp;s=profiles&amp;r=1&amp;esab=a&amp;redirectslug=profiles-tb#w_finding-your-profile-without-opening-thunderbird">that
article</a> is useful.</p>

<h2>Putting together a (bash) script</h2>

<p>Then, you can create a script somewhere, that you will execute when you want to
backup your Thunderbird profile.</p>

<p>I'm on Windows, so I'm going to use the <code>$APPDATA</code> environment variable that is
available in the terminal. I'm using Git Bash's terminal.</p>

<p>This is the script I have done (named <code>backup-thunderbird.sh</code>):</p>

<pre><code>date=$(date -d "today" +"%Y%m%d%H%M")
cp -R $HOME/AppData/Roaming/Thunderbird/Profiles //NAS/Backups/Thunderbird_Profile_Backups/Profile_$date
</code></pre>

<p>And I execute it like so (close thunderbird before doing so. otherwise it won't work):</p>

<pre><code>$ sh backup-thunderbird.sh
</code></pre>

<p>I use a special folder in my NAS server to store the profile backups. But it
could be anything you want, like a Dropbox folder, for example.</p>

<h2>Restoring a backup</h2>

<p>Let's say you reinstall Thunderbird on a new machine. The only things you need
to do in order to restore your backup is:</p>

<ol>
<li>Close Thunderbird, otherwise it'll break something.</li>
<li>(On windows) go to <code>%APPDATA%</code> (you can write it directly in the directory bar of File
Explorer and it will go inside your <code>%HOMEPATH% &gt; AppData &gt; Roaming</code>). From
there, go to <code>Thunderbird &gt; Profiles</code>.</li>
<li>Then, you'll see 2 folders named like:
<code>[randomletters].default</code> and <code>[randomletters].default-release</code>.</li>
<li>Remove the content your the destination's <code>*.default-release/</code> folder.</li>
<li>You can then copy the content of your backup's <code>*.default-release/</code>, then
paste all that into your destination's <code>*.default-release/</code> folder.</li>
</ol>
]]></description>
          </item>
                                <item>
          <title>HTML &amp; CSS courses</title>
            <link>https://www.juliendesrosiers.com/2020/06/05/html-css-courses.php</link>
            <pubDate>Fri, 05 Jun 2020 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2020/06/05/html-css-courses.php</guid>
            <description><![CDATA[<p><img src="zoom.jpg" alt="" /></p>

<p>I like to share my knowledge about tech. And one of the easiest tech to teach
at a level where it can be used in the short term is HTML and CSS.</p>

<p>No high-level concepts. Just layout of documents with code.</p>

<p>Here is what I've done today, for 2 people who joined me on a Zoom meeting.</p>

<h2>The result</h2>

<p><img src="screenshot-formation-resultat.jpg" alt="" /></p>

<h2>What they got</h2>

<ul>
<li>1 hour of live coding action, where they could interrupt me to ask questions
at any time.</li>
<li>Access to the recorded video after the class</li>
<li>Access to the <a href="https://github.com/juliend2/cours-html-css">resulting code</a></li>
<li>Links and references to continue learning</li>
<li>slides</li>
</ul>

<p><img src="syntax-example.jpg" alt="" /></p>

<p>If you are interested in having classes like this one, I offer 1-on-1 and group
classes for subjects ranging from HTML to ruby programming, SQL and database modeling.</p>

<p><a href="https://juliendesrosiers304219.typeform.com/to/JRMzxKNJ">Ask me for a quote now</a>.</p>
]]></description>
          </item>
                                              <item>
          <title>The Psychology of blogs and social networks popularity</title>
            <link>https://www.juliendesrosiers.com/2020/05/21/psychology-social-network-popularity.php</link>
            <pubDate>Thu, 21 May 2020 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2020/05/21/psychology-social-network-popularity.php</guid>
            <description><![CDATA[<p><img src="/2020/05/21/psychology-social-networks.jpg" alt="Contemporary art a Salesperson speaking to a curious crowd, by DALL-E" /></p>

<p>(This post is a response to <a href="https://www.garron.blog/en/blog/blogging.html">that post</a> I <a href="https://news.ycombinator.com/item?id=23237559">found on HN</a>.)</p>

<p>The currency we have right now on Twitter, Instagram and Facebook is <strong>reputation</strong>. There, it's manifested in the form of <em>Followers</em>. That's the very currency that Stack Overflow uses to grow and sustain.</p>

<p>It's "gamified". And the blogosphere needs that, too.</p>

<p>And in a way, blogs have that, but it's pretty much in the form of Reddit and HN and some other sites, where people get their karma fix by taking the time to read the blog post (upfront time investment), comment on it (risk losing karma) and then get their comment being liked (return on investment), which augment their karma (the <strong>reputation currency</strong>).</p>

<p>(Facebook does not have a Karma system like Reddit or HN has. But if it would, it would make the world a bit more like a certain black mirror episode of season 2.)</p>

<p>Why do you see coders put their reddit+HN profile links on their site? Because they want their reputation/karma/notoriety to follow them in real-life context, so it can be converted as real "notoriety currency". (people looking at their profile will say Gosh, this guy has 2000 points on SO and 4000 points on HN, he must be very smart or influent!).</p>

<p>So my guess is that the reason blogs still exist is because of sites like Reddit, HN, where people can get reputation (and not only knowledge) out of their reading. There's also something satisfying in the act of praising a good post with your friends. It's like watching a good movie and talking about how good it is with your friends.</p>

<p>And blogs also survived because it's still being viewed as THE most professional way of expressing one's opinions. It gives you "gravitas" by default, when you have your own blog, compared to having a Twitter / Facebook account like the "plebs" ;-) .</p>

<p>And you get the best of both worlds when you promote your blog posts on social networks.</p>

<p>So why not have a blog, then?</p>
]]></description>
          </item>
                                <item>
          <title>Choosing an entreprise-grade Password Manager</title>
            <link>https://www.juliendesrosiers.com/2020/05/19/choosing-entreprise-password-manager.php</link>
            <pubDate>Tue, 19 May 2020 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2020/05/19/choosing-entreprise-password-manager.php</guid>
            <description><![CDATA[<p>Our IT team at my church was comparing several options for our internal password
management tool. We need to securely share passwords and other sensitive
information with staff and volunteers of pretty much all walks of life.</p>

<h2>The features we needed</h2>

<ul>
<li>Industry-standard encryption algo/level</li>
<li>Cloud-based (We don't want the risky task of managing an on-premise
installation for such a secure component of our internal cybersecurity)</li>
<li>Ease of use</li>
<li>Not too expensive (we're a non-profit)</li>
<li>Mobile app with auto-fill feature integrated at the OS level</li>
<li>Browser extension</li>
<li>Audit log for every shared secret</li>
<li>Groups of users auto-assigned to default permissions specific to collections
of "secrets".</li>
<li>Some flexibility with what we can store (at least blobs of text), but ideally
stuff like identities and payment cards would be useful also.</li>
<li>Sharing passwords to people without them being able to see their content, but
still able to login with auto-fill.</li>
<li>Organization-wide control over minimal password strength criteria.</li>
</ul>

<h3>Nice to haves</h3>

<ul>
<li>Open Source (not so we can host it ourselves, but rather for the fact that
the security is constantly being peer-reviewed by the community of developers)</li>
<li>Linux GUI app</li>
<li>API available</li>
<li>CLI interface</li>
</ul>

<h2>Products we considered</h2>

<ul>
<li>LastPass</li>
<li>1Password</li>
<li>Dashlane</li>
<li>Bitwarden</li>
<li>Keepass</li>
<li>Passwork</li>
</ul>

<h2>Show me what you got</h2>

<table>
<thead>
<tr>
  <th></th>
  <th>LastPass</th>
  <th>1Password</th>
  <th>Dashlane</th>
  <th>Bitwarden</th>
  <th>Keepass</th>
  <th>Passwork</th>
</tr>
</thead>
<tbody>
<tr>
  <td>Industry-standard encryption</td>
  <td>✔</td>
  <td>?</td>
  <td>✔</td>
  <td>✔</td>
  <td>✔</td>
  <td>✔</td>
</tr>
<tr>
  <td>Cloud-based</td>
  <td>✔</td>
  <td>✔</td>
  <td>✔</td>
  <td>✔</td>
  <td>x</td>
  <td>✔</td>
</tr>
<tr>
  <td>Ease of use</td>
  <td>x</td>
  <td>✔</td>
  <td>✔</td>
  <td>✔</td>
  <td>x</td>
  <td>✔</td>
</tr>
<tr>
  <td>Not too expensive</td>
  <td>✔</td>
  <td>✔</td>
  <td>✔</td>
  <td>✔</td>
  <td>✔</td>
  <td>✔</td>
</tr>
<tr>
  <td>Mobile app with auto-fill</td>
  <td>✔</td>
  <td>✔</td>
  <td>✔</td>
  <td>✔</td>
  <td>✔</td>
  <td>x</td>
</tr>
<tr>
  <td>Browser extensions</td>
  <td>✔</td>
  <td>✔</td>
  <td>✔</td>
  <td>✔</td>
  <td>✔</td>
  <td>✔</td>
</tr>
<tr>
  <td>Audit logs</td>
  <td>✔</td>
  <td>✔</td>
  <td>✔</td>
  <td>✔</td>
  <td>?</td>
  <td>✔</td>
</tr>
<tr>
  <td>Groups and Collections</td>
  <td>✔</td>
  <td>✔</td>
  <td>x</td>
  <td>✔</td>
  <td>x</td>
  <td>✔</td>
</tr>
<tr>
  <td>Flexible secret types</td>
  <td>✔</td>
  <td>✔</td>
  <td>✔</td>
  <td>✔</td>
  <td>✔</td>
  <td>✔</td>
</tr>
<tr>
  <td>Sharing logins without people having access to see the password</td>
  <td>✔</td>
  <td>?</td>
  <td>✔</td>
  <td>✔</td>
  <td>x</td>
  <td>?</td>
</tr>
<tr>
  <td>Org-wide control over password criteria</td>
  <td>✔</td>
  <td>✔</td>
  <td>✔</td>
  <td>✔</td>
  <td>x</td>
  <td>x</td>
</tr>
</tbody>
</table>

<h2>Conclusion</h2>

<p>We're currently pretty much settled on Bitwarden despite the fact that
1Password also was a really strong contender. But price gave the final word for
Bitwarden this time.</p>

<!-- vim: set nowrap -->
]]></description>
          </item>
                                <item>
          <title>IRC basics</title>
            <link>https://www.juliendesrosiers.com/2020/05/11/irc-basics.php</link>
            <pubDate>Mon, 11 May 2020 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2020/05/11/irc-basics.php</guid>
            <description><![CDATA[<!-- # IRC -->

<p>Internet Relay Chat</p>

<h1>WARNING ABOUT FREENODE:</h1>

<p>This post refers to Freenode server. I now suggest that you use <a href="https://libera.chat/">Libera
chat</a>
that is the de-facto alternative, now that <a href="https://www.techradar.com/news/major-open-source-projects-abandon-freenode-following-hostile-takeover">Freenode went
nuts</a>.</p>

<hr />

<h2>Concepts</h2>

<ul>
<li>Protocol</li>
<li>Servers</li>
<li>Clients</li>
<li>Commands</li>
</ul>

<h3>Protocol</h3>

<p>IRC is an Internet Protocol, as specified in <a href="https://tools.ietf.org/html/rfc1459">https://tools.ietf.org/html/rfc1459</a></p>

<h3>Servers</h3>

<p>IRC clients connect to IRC servers in order to chat in IRC channels.</p>

<p>IRC Servers form networks of servers:</p>

<p><img src="2020-05-11-11-34-41.png" alt="" /></p>

<p>Source: <a href="https://tools.ietf.org/html/rfc1459#section-1.1">https://tools.ietf.org/html/rfc1459#section-1.1</a></p>

<p>For example, The Freenode IRC network is available through the following domain: irc://chat.freenode.net/</p>

<p>And you can see a list of all the servers associated to freenode's main IRC server, here: <a href="https://netsplit.de/servers/?net=freenode">https://netsplit.de/servers/?net=freenode</a></p>

<h3>Clients</h3>

<p>There's a variety of IRC clients one can use to connect to IRC channels. The most popular is mIRC. In my case, I'm using <a href="https://hexchat.github.io/">HexChat</a> on Windows.</p>

<h3>Commands</h3>

<p>IRC commands can be executed from within any conversation, and they <em>do</em> something that might or might not be outputted for everyone to see.</p>

<p>IRC commands are used to do things like:</p>

<ul>
<li>identifying to a nickserv (<code>/msg NickServ identify &lt;PASSWORD&gt;</code>)</li>
<li>joining a channel (<code>/join #channelname</code>)</li>
<li>quitting a channel (<code>/quit</code>)</li>
</ul>

<p>And more<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>

<h2>In practice</h2>

<p>Now, since I'm mainly going to use IRC for talking about nerdy things (I'm working on Web-related stuff), then I'm going to connect to Freenode IRC network (that has many IRC servers) that hosts mainly FOSS-related IRC channels (chat rooms).</p>

<p><img src="2020-05-11-10-51-58.png" alt="IRC Login" /></p>

<p>After having registering myself to the nickserv, I had to validate my email. So I received the following email:</p>

<p><img src="2020-05-11-10-55-05.png" alt="Freenode's Nickserv validation email" /></p>

<p>Now that you have a user on that IRC network, you can <code>/join</code> a channel.</p>

<p>Here's the command to do so for the <code>#wordpress</code> channel:</p>

<pre><code>/join #wordpress
</code></pre>

<p><img src="2020-05-11-11-21-33.png" alt="" /></p>

<p>Simple!</p>

<h2>Looking for another channel?</h2>

<pre><code>/list
</code></pre>

<p>This command will open a window that looks like this (depending on your IRC client):</p>

<p><img src="2020-05-11-11-26-36.png" alt="Channels List" /></p>

<p>You can then enter a keyword to search through the list. In my case, I'm looking for a GnuPG-related channel.</p>

<p><img src="2020-05-11-11-27-33.png" alt="GPG channel list search" /></p>

<p>So <code>#gnupg</code> looks like the place to go to!</p>

<h2>Security</h2>

<p>Now, you need to know that some people are logging conversations of channels
they are logged into. And some of those people (often the case for open source-related channels)
publish conversations (along with users' IP addresses) on the Web.</p>

<p>Now, you probably don't want your real IP to be listed on the Web, right?</p>

<p>That's when "<a href="https://freenode.net/kb/answer/cloaks">cloaking</a>" become useful.</p>

<p>You can get a cloak by asking your IRC network's #vHost channel, or on
<code>#freenode</code> for example, you just go to the <code>#freenode</code> channel and ask a staff
to give you a cloak that matches your current status. In my case, since I did
not participate in any open source project, I can ask an <a href="https://freenode.net/kb/answer/cloaks#unaffiliated-cloaks">unaffiliated
cloak</a>.</p>

<p>If you're not identified to the nickserv yet, they'll ask you to do so first
hand.</p>

<h2>SASL</h2>

<p>Also, to make sure you're always signed in with your own nickname on the
network you're connecting to, you need to configure your client so that it
auto-connects instead of you always typing <code>/msg nickserv identify ...</code> each
time.</p>

<p><img src="2020-05-16-20-32-22.png" alt="" /></p>

<h2>Credits</h2>

<p>Thanks to <code>indomitable</code> on Freenode, who helped me figure out and clarify some
of those aspects.</p>

<div class="footnotes" role="doc-endnotes">
<hr />
<ol>

<li id="fn:1" role="doc-endnote">
<p>The commands:
- <a href="https://duckduckgo.com/?q=irc+commands&amp;ia=cheatsheet&amp;iax=1">https://duckduckgo.com/?q=irc+commands&amp;ia=cheatsheet&amp;iax=1</a>
- <a href="http://www.cheat-sheets.org/saved-copy/wikiHow_IRC_Cheat_Sheet.pdf">http://www.cheat-sheets.org/saved-copy/wikiHow_IRC_Cheat_Sheet.pdf</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#8617;&#xFE0E;</a></p>
</li>

</ol>
</div>
]]></description>
          </item>
                                <item>
          <title>PGP pour des emails sécuritaires</title>
            <link>https://www.juliendesrosiers.com/2020/05/09/pgp-emails-securitaires.php</link>
            <pubDate>Sat, 09 May 2020 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2020/05/09/pgp-emails-securitaires.php</guid>
            <description><![CDATA[<!-- # PGP pour des emails sécuritaires-->

<h2>Vidéo d'introduction</h2>

<div class="video-container">
<iframe  src="https://www.youtube-nocookie.com/embed/C0kOB_nX10I" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>

<p><br></p>

<h2>Table de matières</h2>

<ol>
<li>Le problème (insécurité des emails)</li>
<li>La solution (signatures GPG)

<ol>
<li>Key pairs (privée et publique)</li>
<li>Identités</li>
<li>Key Servers</li>
<li>Signature de emails</li>
<li>Encryption de emails</li>
</ol></li>
</ol>

<h2>Le problème</h2>

<p>Imagine une seconde que quelqu'un veut se faire passer pour ton boss (ou autre autorité), afin de te faire faire quelque chose qui compromet la sécurité de l'organisation. Par exemple:</p>

<ol>
<li>Il va sur https://lachapelle.me/contact/</li>
<li>Il trouve le email de David Pothier (le pasteur en chef pour qui tu fais du bénévolat): dpothier@lachapelle.me</li>
<li>Il code un script PHP ou autre, afin d'envoyer un email à un des programmeurs (toi) pour avoir des infos cruciales ("social engineering", baby).</li>
<li>Il a préalablement enregistré un domaine similaire à lachapelle.me, où il a créé une adresse email similaire à celle de David, où il recevra ta réponse.</li>
<li>Il exécute le script, et attend patiemment que ta psychologie faillible fasse son oeuvre.</li>
</ol>

<p>Voici le script en question :</p>

<pre><code>&lt;?php
$to      = 'hello@juliendesrosiers.com';
$subject = "Besoin urgent";
$headers = 'From: dpothier@lachapelle.me' . "\r\n" .
'Reply-To: dpothier@lachapele.me' . "\r\n" .
'X-Mailer: PHP/' . phpversion();

$message = "J'ai une modification urgente à faire par un nouveau développeur
bénévole, donc si tu peux me donner le user et mot de passe pour l'admin du
WordPress de la Chapelle.

Merci d'avance de ta rapidité !

David";

mail($to, $subject, $message, $headers);
</code></pre>

<p>On exécute ce script, et voici ce qui apparaît dans notre boîte de réception :</p>

<p><img src="david-pothier-email.png" alt="Fake email" /></p>

<p>Pourquoi c'est possible ? Parce que le protocole des emails n'est pas sécurisé à la base.</p>

<p><img src="david-pothier-reponse.png" alt="Reponse" /></p>

<p>Avez-vous spotté l'erreur ?</p>

<p>Oui, le domaine (qui est fournis par le <code>Reply-To</code>) lachapele.me (un seul l) est disponible au moment d'écrire ces lignes, donc le hacker aura préalablement enregistré ce domaine, et ajouté un email dpothier@ à celle-ci. Donc si le destinataire répond, ça va envoyer à cette adresse-là, qui est assez similaire, qui est un hacker. Et ça risque de passer complètement inapperçu.</p>

<p>Parce que même David ne recevra pas ta réponse, parce que le Reply-To est différente, quoi que semblable.</p>

<p>Et pendant que tu es stressé à répondre promptement à un email urgent d'une autorité, la dernière chose que tu va checker c'est si le email de réponse est bien valide.</p>

<h2>La solution</h2>

<p>PGP : Pretty Good Privacy.
À ne pas confondre avec GPG (GNU Privacy Guard), qui fait à peu près la même chose, mais est open source.</p>

<h3>Key pairs</h3>

<p>Des clés RSA, DSA, et autres algorithmes de ce genre.</p>

<p>Dans <em>tous les cas</em>, cette paire de clés est formée d'une clé <strong>privée</strong> et d'une clé <strong>publique</strong> qui y est associée.</p>

<p>La clé privée, biensûr, doit rester secrète, comme un mot de passe. Et la clé publique peut être partagée autant que nécessaire.</p>

<h3>Identités</h3>

<p>Une clé (constituée d'une version privée et d'une contrepartie publique) peut être associée à plusieurs emails (et facultativement, couplé du nom de son propriétaire), qui sont associées à une même personne.</p>

<p>Voici un exemple:</p>

<p><img src="cles.png" alt="Cles" /></p>

<p>On peut voir par exemple que Francis a plusieurs clés qui sont assignées à plusieurs adresses emails.</p>

<p>Et Julien, pour sa part, a lié plusieurs adresses email à la même clé, parce qu'il considèrent qu'elles représentent la même identité/personne.</p>

<p><img src="identites.png" alt="Identites" /></p>

<h3>Key Servers</h3>

<h4>Pourquoi on a besoin des Key Servers ?</h4>

<p>De la même manière qu'un ROOT Certificate Authority est nécessaire pour l'infrastructure de certificats TLS pour les sites Web, il est nécessaire d'avoir des serveurs qui servent à stocker des clés publiques, et à permettre à des gens de :</p>

<ol>
<li>trouver la clé publique de quelqu'un afin de décrypter un email qu'il nous a envoyé, ou de valider que la signature est bien la sienne.</li>
<li>certifier qu'une clé est bien valide.</li>
</ol>

<h4>Pourquoi les Key Servers ne sont pas suffisants ?</h4>

<p>Par exemple, si on regarde la clé PGP de Richard Stallman, on peut voir qu'il en a plusieurs, parce que des trolls ont trouvé drôle de créer des fakes.</p>

<p>C'est pourquoi il dit sur son site:</p>

<blockquote>
  <p>If you want to send me GPG-encrypted mail, do not trust key servers! Some of them have phony keys under my name and email address, made by someone else as a trick. See <a href="https://stallman.org/gpg.html">gpg.html</a> for my real key.</p>
</blockquote>

<p>C'est pas fou. Parce qu'en effet, n'importe qui peut créer des fausses clés et les uploader sur des key servers:</p>

<p><img src="richard-stallman-gpg.png" alt="Richard Stallman's fake PGP keys" /></p>

<p>Et si ces personnes utilisent cette fausse identité (enregistrée et soumise au keyserver par quelqu'un qui n'est PAS Richard Stallman), elle va avoir son email apparaître comme étant signé par Richard Stallman. Seulement, ce n'est pas la bonne clé qui a été utilisée.</p>

<p>Exemple d'un email qui est valide, selon l'extension Enigmail dans Thunderbird:</p>

<p><img src="signed-email.png" alt="Signed Email" /></p>

<p>C'est un peu comme si quelqu'un créait un site de fishing avec le vrai nom de domaine de ta banque, et bypassait ton routeur pour rediriger les requêtes de desjardins.com vers l'IP de son serveur de fishing, mais qu'EN PLUS DE ÇA, il avait un certificat SSL que ton browser voit comme étant légitime (petit cadenas vert dans la barre d'adresse).</p>

<p>Ça c'est la petite faiblesse de PGP. C'est pourquoi on a besoin non seulement des key servers, mais aussi d'une manière d'être sûr qu'un clé publique est bien celle de la personne qui nous contacte, et pas de quelqu'un d'autre.</p>

<p>C'est entre autre pour ça que Keybase.io (tsé, la compagnie <a href="https://www.cnbc.com/2020/05/07/zoom-buys-keybase-in-first-deal-as-part-of-plan-to-fix-security.html">que Zoom a acheté</a> ?) a été créé; ça permet de partager sa clé publique, tout en prouvant, au même endroit, que c'est la bonne, via des preuves vérifiées par keybase, tel qu'un fichier que tu héberges sur github, un tweet qu'il a publié à partir de ton compte twitter, etc.</p>

<p><img src="keybase-pascal.png" alt="Keybase de Pascal" /></p>

<p>Ici on peut vérifier chacune de ses preuves, pour valider soi-même que la key fingerprint (<code>9521 927B 5582 0DA2</code>) -- qui est un hash de la clé publique -- appartient bien à LUI. Donc on peut lui faire confiance, et même télécharger sa clé publique pour l'ajouter à notre keychain (logiciel qui gère les clés de nos contacts de confiance).</p>

<h3>Encryption des emails</h3>

<p>TODO</p>

<h2>Conclusion</h2>

<p>J'espère qu'avec ces infos, vous aurez une meilleure idée de pourquoi PGP est encore pertinent aujourd'hui.</p>

<p>Et encore, je n'ai pas effleuré le sujet de l'encryption des messages, car c'est une autre des fonctionalités de PGP.</p>

<h2>Remerciements</h2>

<p>Merci à <a href="https://pascalmeunier.info/">Pascal Meunier</a> de <a href="https://www.hivetek.com/">HiveTek</a> pour sa relecture et son feedback.</p>
]]></description>
          </item>
                                              <item>
          <title>My system for managing data, operations and events of my life.</title>
            <link>https://www.juliendesrosiers.com/2020/04/04/system-for-managing-data-operations-events-of-life.php</link>
            <pubDate>Sat, 04 Apr 2020 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2020/04/04/system-for-managing-data-operations-events-of-life.php</guid>
            <description><![CDATA[[...]]]></description>
          </item>
                                              <item>
          <title>Librairies PHP intéressantes</title>
            <link>https://www.juliendesrosiers.com/2020/03/06/librairies-php-interessantes.php</link>
            <pubDate>Fri, 06 Mar 2020 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2020/03/06/librairies-php-interessantes.php</guid>
            <description><![CDATA[[...]]]></description>
          </item>
                                <item>
          <title>Comment j&#039;ai commencé</title>
            <link>https://www.juliendesrosiers.com/2016/07/01/comment-jai-commence.php</link>
            <pubDate>Fri, 01 Jul 2016 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2016/07/01/comment-jai-commence.php</guid>
            <description><![CDATA[[...]]]></description>
          </item>
                                <item>
          <title>WordPress plugins for complex websites</title>
            <link>https://www.juliendesrosiers.com/2012/11/20/wordpress-plugins-for-complex-websites.php</link>
            <pubDate>Tue, 20 Nov 2012 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2012/11/20/wordpress-plugins-for-complex-websites.php</guid>
            <description><![CDATA[[...]]]></description>
          </item>
                                <item>
          <title>The WordPress database diagram</title>
            <link>https://www.juliendesrosiers.com/2012/11/08/the-wordpress-database-diagram.php</link>
            <pubDate>Thu, 08 Nov 2012 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2012/11/08/the-wordpress-database-diagram.php</guid>
            <description><![CDATA[[...]]]></description>
          </item>
                                <item>
          <title>(WordPress) Automatically prefix Custom Post Type slugs with the Author name</title>
            <link>https://www.juliendesrosiers.com/2012/10/27/automatically-prefix-custom-post-type-slugs-author-name.php</link>
            <pubDate>Sat, 27 Oct 2012 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2012/10/27/automatically-prefix-custom-post-type-slugs-author-name.php</guid>
            <description><![CDATA[[...]]]></description>
          </item>
                                <item>
          <title>My best practices for local WordPress development</title>
            <link>https://www.juliendesrosiers.com/2012/05/30/best-practices-local-wordpress-development.php</link>
            <pubDate>Wed, 30 May 2012 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2012/05/30/best-practices-local-wordpress-development.php</guid>
            <description><![CDATA[[...]]]></description>
          </item>
                                <item>
          <title>How to: test web applications with the Android SDK on Mac OSX Lion</title>
            <link>https://www.juliendesrosiers.com/2012/03/30/how-to-test-web-applications-with-the-android-sdk-on-mac-osx-lion.php</link>
            <pubDate>Fri, 30 Mar 2012 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2012/03/30/how-to-test-web-applications-with-the-android-sdk-on-mac-osx-lion.php</guid>
            <description><![CDATA[[...]]]></description>
          </item>
                                <item>
          <title>New side project: The Daily WordPress Reference</title>
            <link>https://www.juliendesrosiers.com/2012/03/10/the-daily-wordpress-reference-newsletter.php</link>
            <pubDate>Sat, 10 Mar 2012 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2012/03/10/the-daily-wordpress-reference-newsletter.php</guid>
            <description><![CDATA[[...]]]></description>
          </item>
                                <item>
          <title>Build an Automator script to upload images to your server</title>
            <link>https://www.juliendesrosiers.com/2011/11/19/build-an-automator-script-to-upload-images-to-your-server.php</link>
            <pubDate>Sat, 19 Nov 2011 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2011/11/19/build-an-automator-script-to-upload-images-to-your-server.php</guid>
            <description><![CDATA[[...]]]></description>
          </item>
                                <item>
          <title>YegorDyachkov.com</title>
            <link>https://www.juliendesrosiers.com/2011/09/18/yegordyachkov-com.php</link>
            <pubDate>Sun, 18 Sep 2011 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2011/09/18/yegordyachkov-com.php</guid>
            <description><![CDATA[[...]]]></description>
          </item>
                                <item>
          <title>Run Gollum on Pow</title>
            <link>https://www.juliendesrosiers.com/2011/07/20/run-gollum-on-pow.php</link>
            <pubDate>Wed, 20 Jul 2011 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2011/07/20/run-gollum-on-pow.php</guid>
            <description><![CDATA[[...]]]></description>
          </item>
                                <item>
          <title>JulienMP3Player: an MP3 Player jQuery plugin (Yes, in JavaScript!)</title>
            <link>https://www.juliendesrosiers.com/2011/04/02/julienmp3player.php</link>
            <pubDate>Sat, 02 Apr 2011 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2011/04/02/julienmp3player.php</guid>
            <description><![CDATA[[...]]]></description>
          </item>
                                <item>
          <title>Auto-Generating CRUDs in Sinatra, for fun and profit</title>
            <link>https://www.juliendesrosiers.com/2011/03/21/auto-generating-cruds-in-sinatra-for-fun-and-profit.php</link>
            <pubDate>Mon, 21 Mar 2011 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2011/03/21/auto-generating-cruds-in-sinatra-for-fun-and-profit.php</guid>
            <description><![CDATA[[...]]]></description>
          </item>
                                <item>
          <title>Fixing the “bundled mysql.rb driver has been removed from Rails 2.2″ error</title>
            <link>https://www.juliendesrosiers.com/2010/12/25/fixing-the-the-bundled-mysql-rb-driver-has-been-removed-from-rails-2-2-error.php</link>
            <pubDate>Sat, 25 Dec 2010 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2010/12/25/fixing-the-the-bundled-mysql-rb-driver-has-been-removed-from-rails-2-2-error.php</guid>
            <description><![CDATA[[...]]]></description>
          </item>
                                <item>
          <title>KahnawakeVoices.com</title>
            <link>https://www.juliendesrosiers.com/2010/11/19/kahnawakevoices-com.php</link>
            <pubDate>Fri, 19 Nov 2010 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2010/11/19/kahnawakevoices-com.php</guid>
            <description><![CDATA[[...]]]></description>
          </item>
                                <item>
          <title>Saving hours with Anemone and Nokogiri</title>
            <link>https://www.juliendesrosiers.com/2010/11/08/saving-hours-with-anemone-and-nokogiri.php</link>
            <pubDate>Mon, 08 Nov 2010 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2010/11/08/saving-hours-with-anemone-and-nokogiri.php</guid>
            <description><![CDATA[[...]]]></description>
          </item>
                                <item>
          <title>Announcing my first jQuery plugin: JulienRSSWidget</title>
            <link>https://www.juliendesrosiers.com/2010/10/22/announcing-my-first-jquery-plugin-julienrsswidget.php</link>
            <pubDate>Fri, 22 Oct 2010 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2010/10/22/announcing-my-first-jquery-plugin-julienrsswidget.php</guid>
            <description><![CDATA[[...]]]></description>
          </item>
                                <item>
          <title>howto bootstrap your own server at home (with a dynamic IP)</title>
            <link>https://www.juliendesrosiers.com/2010/10/14/howto-bootstrap-your-own-server-at-home-with-dynamic-ip.php</link>
            <pubDate>Thu, 14 Oct 2010 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2010/10/14/howto-bootstrap-your-own-server-at-home-with-dynamic-ip.php</guid>
            <description><![CDATA[[...]]]></description>
          </item>
                                <item>
          <title>MontrealHackers.com</title>
            <link>https://www.juliendesrosiers.com/2010/09/10/montrealhackers-com.php</link>
            <pubDate>Fri, 10 Sep 2010 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2010/09/10/montrealhackers-com.php</guid>
            <description><![CDATA[[...]]]></description>
          </item>
                                <item>
          <title>Fixing ActionView::SafeBuffer deprecation warning with Ckeditor in Rails 2.3</title>
            <link>https://www.juliendesrosiers.com/2010/08/18/fixing-actionviewsafebuffer-deprecation-warning-with-ckeditor-in-rails-2-3.php</link>
            <pubDate>Wed, 18 Aug 2010 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2010/08/18/fixing-actionviewsafebuffer-deprecation-warning-with-ckeditor-in-rails-2-3.php</guid>
            <description><![CDATA[[...]]]></description>
          </item>
                                <item>
          <title>bash script to zip a folder while ignoring git files and copying it to dropbox</title>
            <link>https://www.juliendesrosiers.com/2010/06/30/bash-script-to-zip-a-folder-while-ignoring-git-files-and-copying-it-to-dropbox.php</link>
            <pubDate>Wed, 30 Jun 2010 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2010/06/30/bash-script-to-zip-a-folder-while-ignoring-git-files-and-copying-it-to-dropbox.php</guid>
            <description><![CDATA[[...]]]></description>
          </item>
                                <item>
          <title>Déployer une application Lua (orbit) sur Webfaction</title>
            <link>https://www.juliendesrosiers.com/2010/05/09/deployer-une-application-lua-orbit-sur-webfaction.php</link>
            <pubDate>Sun, 09 May 2010 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2010/05/09/deployer-une-application-lua-orbit-sur-webfaction.php</guid>
            <description><![CDATA[[...]]]></description>
          </item>
                                <item>
          <title>Backuper toutes mes DB MySQL sur un serveur distant</title>
            <link>https://www.juliendesrosiers.com/2010/03/07/backuper-toutes-mes-db-mysql-sur-un-serveur-distant.php</link>
            <pubDate>Sun, 07 Mar 2010 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2010/03/07/backuper-toutes-mes-db-mysql-sur-un-serveur-distant.php</guid>
            <description><![CDATA[[...]]]></description>
          </item>
                                <item>
          <title>Intro à PhoneGap</title>
            <link>https://www.juliendesrosiers.com/2009/03/25/intro-a-phonegap.php</link>
            <pubDate>Wed, 25 Mar 2009 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2009/03/25/intro-a-phonegap.php</guid>
            <description><![CDATA[[...]]]></description>
          </item>
                                <item>
          <title>Charger une image en Actionscript 3</title>
            <link>https://www.juliendesrosiers.com/2008/09/25/charger-une-image-en-actionscript-3.php</link>
            <pubDate>Thu, 25 Sep 2008 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2008/09/25/charger-une-image-en-actionscript-3.php</guid>
            <description><![CDATA[[...]]]></description>
          </item>
                                <item>
          <title>Accéder à son serveur à partir de l’extérieur</title>
            <link>https://www.juliendesrosiers.com/2008/09/14/acceder-a-son-serveur-a-partir-de-lexterieur.php</link>
            <pubDate>Sun, 14 Sep 2008 00:00:00 +0000</pubDate>

            <guid>https://www.juliendesrosiers.com/2008/09/14/acceder-a-son-serveur-a-partir-de-lexterieur.php</guid>
            <description><![CDATA[[...]]]></description>
          </item>
                  </channel>
  </rss>

  