{
  "version": "https://jsonfeed.org/version/1.1",
  "title": "Notes — msfjarvis.dev",
  "description": "Short notes by Harsh Shandilya",
  "home_page_url": "https://msfjarvis.dev/",
  "feed_url": "https://msfjarvis.dev/notes/feed.json",
  "authors": [
    {
      "name": "Harsh Shandilya",
      "url": "https://msfjarvis.dev"
    }
  ],
  "items": [
    {
      "id": "https://msfjarvis.dev/notes/how-to-dig-up-historical-execution-logs-from-systemd/",
      "url": "https://msfjarvis.dev/notes/how-to-dig-up-historical-execution-logs-from-systemd/",
      "title": "How to dig up historical execution logs from systemd",
      "date_published": "2026-07-16T17:40:00.000Z",
      "content_html": "<p>While reporting a bug to a project which was failing to run on my server, I had to go and dig up the systemd logs for it from ~18 hours ago which had since been buried by normal runtime logs (I had worked around the issue). Since I’ve been trying to wean myself off reaching for the LLM crutch all the time, here’s a short rundown of my spelunking to find how to do this.</p>\n<p><a href=\"https://unix.stackexchange.com/questions/506833/show-journal-logs-from-the-time-a-service-was-restarted\">This Unix StackExchange question</a> introduce me to <code>journalctl INVOCATION_ID=$id + _SYSTEMD_INVOCATION_ID=$id</code> as a way to get logs for a specific “run” of a systemd service. Problem: <code>systemctl show -p InvocationID --value my-unit.service</code> shows the <em>current</em> invocation ID which wasn’t what I needed. I want an older one, when the service was failing.</p>\n<p>I continued searching around, and came across <a href=\"https://jwodder.github.io/kbits/posts/systemd-logs\">this excellent article</a> which also explains how the incantation above actually works. To dig up historical invocation IDs, this shell pipeline can pull out start/stop events and collate them to invocation IDs — exactly what was needed here!</p>\n<div class=\"expressive-code\"><link rel=\"stylesheet\" href=\"https://msfjarvis.dev/_astro/ec.cteup.css\"><figure class=\"frame\"><figcaption class=\"header\"></figcaption><pre data-language=\"plain\"><code><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">journalctl -o json -t systemd -u $service | jq -r '\"[\\(.__REALTIME_TIMESTAMP|tonumber / 1000000 | todate)] [\\(.INVOCATION_ID)] \\(.MESSAGE)\"'</span></div></div></code></pre><div class=\"copy\"><div aria-live=\"polite\"></div><button title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"journalctl -o json -t systemd -u $service | jq -r '&quot;[\\(.__REALTIME_TIMESTAMP|tonumber / 1000000 | todate)] [\\(.INVOCATION_ID)] \\(.MESSAGE)&quot;'\"><div></div></button></div></figure></div>\n<p>This is actually pretty simple when you break it down: get the systemd logs for the specified service in JSON format; then pull out the <code>.__REALTIME_TIMESTAMP</code>, <code>.INVOCATION_ID</code>, and <code>.MESSAGE</code> fields from each line. This let me get the logs I was looking for</p>\n<div class=\"expressive-code\"><figure class=\"frame\"><figcaption class=\"header\"></figcaption><pre data-language=\"plain\"><code><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">[2026-07-15T19:16:54Z] [b517a64c63744438bafdff79fefbcc50] Stopping atticd.service...</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">[2026-07-15T19:16:54Z] [b517a64c63744438bafdff79fefbcc50] atticd.service: Deactivated successfully.</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">[2026-07-15T19:16:54Z] [b517a64c63744438bafdff79fefbcc50] Stopped atticd.service.</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">[2026-07-15T19:16:54Z] [b517a64c63744438bafdff79fefbcc50] atticd.service: Consumed 2min 16.326s CPU time over 1w 3d 10h 18min 28.002s wall clock time, 119.3M memory peak, 13.8M read from disk, 9.2G incoming IP traffic, 5.5G outgoing IP traffic.</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">[2026-07-15T19:17:02Z] [ff392b40208744748a9a1332766d9ef7] Started atticd.service.</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">[2026-07-15T19:17:02Z] [ff392b40208744748a9a1332766d9ef7] atticd.service: Main process exited, code=exited, status=1/FAILURE</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">[2026-07-15T19:17:02Z] [ff392b40208744748a9a1332766d9ef7] atticd.service: Failed with result 'exit-code'.</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">[2026-07-15T19:17:03Z] [ff392b40208744748a9a1332766d9ef7] atticd.service: Scheduled restart job immediately on client request, restart counter is at 1.</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">[2026-07-15T19:17:03Z] [a026e6feb1e14c52b2afaab92e9b8432] Started atticd.service.</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">[2026-07-15T19:17:03Z] [a026e6feb1e14c52b2afaab92e9b8432] atticd.service: Main process exited, code=exited, status=1/FAILURE</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">[2026-07-15T19:17:03Z] [a026e6feb1e14c52b2afaab92e9b8432] atticd.service: Failed with result 'exit-code'.</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">[2026-07-15T19:17:13Z] [a026e6feb1e14c52b2afaab92e9b8432] atticd.service: Scheduled restart job, restart counter is at 2.</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">[2026-07-15T19:17:13Z] [aaf93ea69cb64ad4987649c8f0d42cdb] Started atticd.service.</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">[2026-07-15T19:17:13Z] [aaf93ea69cb64ad4987649c8f0d42cdb] atticd.service: Main process exited, code=exited, status=1/FAILURE</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">[2026-07-15T19:17:13Z] [aaf93ea69cb64ad4987649c8f0d42cdb] atticd.service: Failed with result 'exit-code'.</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">[2026-07-15T19:17:23Z] [aaf93ea69cb64ad4987649c8f0d42cdb] atticd.service: Scheduled restart job, restart counter is at 3.</span></div></div></code></pre><div class=\"copy\"><div aria-live=\"polite\"></div><button title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"[2026-07-15T19:16:54Z] [b517a64c63744438bafdff79fefbcc50] Stopping atticd.service...[2026-07-15T19:16:54Z] [b517a64c63744438bafdff79fefbcc50] atticd.service: Deactivated successfully.[2026-07-15T19:16:54Z] [b517a64c63744438bafdff79fefbcc50] Stopped atticd.service.[2026-07-15T19:16:54Z] [b517a64c63744438bafdff79fefbcc50] atticd.service: Consumed 2min 16.326s CPU time over 1w 3d 10h 18min 28.002s wall clock time, 119.3M memory peak, 13.8M read from disk, 9.2G incoming IP traffic, 5.5G outgoing IP traffic.[2026-07-15T19:17:02Z] [ff392b40208744748a9a1332766d9ef7] Started atticd.service.[2026-07-15T19:17:02Z] [ff392b40208744748a9a1332766d9ef7] atticd.service: Main process exited, code=exited, status=1/FAILURE[2026-07-15T19:17:02Z] [ff392b40208744748a9a1332766d9ef7] atticd.service: Failed with result 'exit-code'.[2026-07-15T19:17:03Z] [ff392b40208744748a9a1332766d9ef7] atticd.service: Scheduled restart job immediately on client request, restart counter is at 1.[2026-07-15T19:17:03Z] [a026e6feb1e14c52b2afaab92e9b8432] Started atticd.service.[2026-07-15T19:17:03Z] [a026e6feb1e14c52b2afaab92e9b8432] atticd.service: Main process exited, code=exited, status=1/FAILURE[2026-07-15T19:17:03Z] [a026e6feb1e14c52b2afaab92e9b8432] atticd.service: Failed with result 'exit-code'.[2026-07-15T19:17:13Z] [a026e6feb1e14c52b2afaab92e9b8432] atticd.service: Scheduled restart job, restart counter is at 2.[2026-07-15T19:17:13Z] [aaf93ea69cb64ad4987649c8f0d42cdb] Started atticd.service.[2026-07-15T19:17:13Z] [aaf93ea69cb64ad4987649c8f0d42cdb] atticd.service: Main process exited, code=exited, status=1/FAILURE[2026-07-15T19:17:13Z] [aaf93ea69cb64ad4987649c8f0d42cdb] atticd.service: Failed with result 'exit-code'.[2026-07-15T19:17:23Z] [aaf93ea69cb64ad4987649c8f0d42cdb] atticd.service: Scheduled restart job, restart counter is at 3.\"><div></div></button></div></figure></div>\n<p>And with one of those invocation IDs:</p>\n<div class=\"expressive-code\"><figure class=\"frame\"><figcaption class=\"header\"></figcaption><pre data-language=\"plain\"><code><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">$ journalctl _SYSTEMD_INVOCATION_ID=7f6d9e0339444adaad8460580ab6cd2c</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">Jul 16 06:57:12 melody atticd[471429]: Attic Server 0.1.0 (release)</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">Jul 16 06:57:12 melody atticd[471429]: 2026-07-16T01:27:12.730131Z  INFO attic_server::config: Using configurations: \"/nix/store/zbx2sq3s9399pgyhrcq8hswq1ialyl15-checked-attic-server.toml\"</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">Jul 16 06:57:12 melody atticd[471429]: Running migrations...</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">Jul 16 06:57:12 melody atticd[471429]: 2026-07-16T01:27:12.731051Z DEBUG sqlx_postgres::options::pgpass: `.pgpass` file not found path=/.pgpass</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">Jul 16 06:57:12 melody atticd[471429]: Error: Database error: Connection Error: error returned from database: Peer authentication failed for user \"anonymous\" at line 334: error returned from database: Peer authentication failed for user \"anonymous\" at line 334</span></div></div></code></pre><div class=\"copy\"><div aria-live=\"polite\"></div><button title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"$ journalctl _SYSTEMD_INVOCATION_ID=7f6d9e0339444adaad8460580ab6cd2cJul 16 06:57:12 melody atticd[471429]: Attic Server 0.1.0 (release)Jul 16 06:57:12 melody atticd[471429]: 2026-07-16T01:27:12.730131Z  INFO attic_server::config: Using configurations: &quot;/nix/store/zbx2sq3s9399pgyhrcq8hswq1ialyl15-checked-attic-server.toml&quot;Jul 16 06:57:12 melody atticd[471429]: Running migrations...Jul 16 06:57:12 melody atticd[471429]: 2026-07-16T01:27:12.731051Z DEBUG sqlx_postgres::options::pgpass: `.pgpass` file not found path=/.pgpassJul 16 06:57:12 melody atticd[471429]: Error: Database error: Connection Error: error returned from database: Peer authentication failed for user &quot;anonymous&quot; at line 334: error returned from database: Peer authentication failed for user &quot;anonymous&quot; at line 334\"><div></div></button></div></figure></div>\n<p>Thank you John T. Wodder II for your invaluable resource! They’ve written <a href=\"https://jwodder.github.io/kbits/authors/john-t-wodder-ii/\">many such posts</a> that collect information which is entirely too difficult to discover for no good reason.</p>"
    },
    {
      "id": "https://msfjarvis.dev/notes/debugging-how-i-accidentally-blocked-someone-from-using-my-app/",
      "url": "https://msfjarvis.dev/notes/debugging-how-i-accidentally-blocked-someone-from-using-my-app/",
      "title": "Debugging how I accidentally blocked someone from using my app",
      "date_published": "2026-07-13T15:20:23.598Z",
      "summary": "How an old anti-crawler Caddy rule came back to bite me",
      "content_html": "<p>I received a <a href=\"https://github.com/msfjarvis/compose-lobsters/issues/1234\">bug report</a> today from a user of <a href=\"https://github.com/msfjarvis/compose-lobsters\">Claw</a>, my Android app for the <a href=\"https://lobste.rs\">Lobsters</a> microblogging community.\nThey complained that the app refused to work for them over their VPN because it was causing the <a href=\"https://github.com/cashapp/zipline\">Zipline</a> manifest to fail to load. <small>(I <a href=\"https://github.com/msfjarvis/compose-lobsters/commit/2c9fc7e5dc8f210157beaeef0661f11a906160e0\">made that fallible</a> separately)</small></p>\n<p>I had a false start looking at Cloudflare as the likely culprit, but thanks to that I was at least able to recover the IP address for the failing request. Correlating that to my web server logs made the problem immediately obvious.</p>\n<div class=\"expressive-code\"><link rel=\"stylesheet\" href=\"https://msfjarvis.dev/_astro/ec.cteup.css\"><figure class=\"frame\"><figcaption class=\"header\"></figcaption><pre data-language=\"plaintext\"><code><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">[root@wailord:/var/log/caddy]# grep -r {redacted.ip} access-claw.msfjarvis.dev.log</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">{redacted.ip} - - [1783746921] \"GET /current/manifest.zipline.json HTTP/2.0\" 0 0</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">{redacted.ip} - - [1783746923] \"GET /current/manifest.zipline.json HTTP/2.0\" 0 0</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">{redacted.ip} - - [1783746925] \"GET /current/manifest.zipline.json HTTP/2.0\" 0 0</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">{redacted.ip} - - [1783746927] \"GET /current/manifest.zipline.json HTTP/2.0\" 0 0</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">{redacted.ip} - - [1783746928] \"GET /current/manifest.zipline.json HTTP/2.0\" 0 0</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">{redacted.ip} - - [1783746932] \"GET /current/manifest.zipline.json HTTP/2.0\" 0 0</span></div></div></code></pre><div class=\"copy\"><div aria-live=\"polite\"></div><button title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"[root@wailord:/var/log/caddy]# grep -r {redacted.ip} access-claw.msfjarvis.dev.log{redacted.ip} - - [1783746921] &quot;GET /current/manifest.zipline.json HTTP/2.0&quot; 0 0{redacted.ip} - - [1783746923] &quot;GET /current/manifest.zipline.json HTTP/2.0&quot; 0 0{redacted.ip} - - [1783746925] &quot;GET /current/manifest.zipline.json HTTP/2.0&quot; 0 0{redacted.ip} - - [1783746927] &quot;GET /current/manifest.zipline.json HTTP/2.0&quot; 0 0{redacted.ip} - - [1783746928] &quot;GET /current/manifest.zipline.json HTTP/2.0&quot; 0 0{redacted.ip} - - [1783746932] &quot;GET /current/manifest.zipline.json HTTP/2.0&quot; 0 0\"><div></div></button></div></figure></div>\n<p>This bug report made me remember that in pursuit of what ultimately became the <a href=\"https://msfjarvis.dev/posts/keeping-ai-crawlers-off-my-forgejo-server/\">Keeping AI crawlers off my Forgejo server</a> post, I had tried blocking all data centers from my servers. <a href=\"https://github.com/JasonLovesDoggo/caddy-defender\">caddy-defender</a> comes with a handy way to drop traffic from a list of providers based on their published IP ranges, which I used for this purpose. I have <a href=\"https://github.com/msfjarvis/dotfiles/blob/0ae95ccc9f53def118bdaafecb2f73bd97cb4ae6/modules/nixos/caddy/default.nix#L120-L124\">this reusable snippet</a> in my NixOS configuration that I default to applying on all public-facing sites. This was causing the user’s requests to get terminated, and the fix was as simple as <a href=\"https://github.com/msfjarvis/dotfiles/commit/0ae95ccc9f53def118bdaafecb2f73bd97cb4ae6\">just removing that rule</a>.</p>\n<p>Fun way to wind down an evening!</p>"
    },
    {
      "id": "https://msfjarvis.dev/notes/til-grafana-labs-used-to-be-called-raintank/",
      "url": "https://msfjarvis.dev/notes/til-grafana-labs-used-to-be-called-raintank/",
      "title": "TIL: Grafana Labs used to be called raintank",
      "date_published": "2026-06-25T17:51:00.000Z",
      "content_html": "<p>While trying to share a <a href=\"https://grafana.com\">Grafana</a> dashboard today I saw an unfamiliar option: <code>Publish to snapshots.raintank.io</code> which led me to a small goose chase. Thankfully that option wasn’t the result of some malicious plugin, it’s just Grafana Labs’ <a href=\"https://grafana.com/blog/we-are-now-grafana-labs/\">old name</a>.</p>"
    },
    {
      "id": "https://msfjarvis.dev/notes/til-filtering-files-in-gallery-dl/",
      "url": "https://msfjarvis.dev/notes/til-filtering-files-in-gallery-dl/",
      "title": "TIL: Filtering files in gallery-dl",
      "date_published": "2026-06-16T11:12:00.000Z",
      "content_html": "<p>You can filter for things like file names using the <code>--filter</code> option in <a href=\"https://codeberg.org/mikf/gallery-dl\">gallery-dl</a> which takes a Python expression. <code>gallery-dl &lt;URL&gt; --filter \"'Thing' in filename\"</code> is a valid filter, but I couldn’t find a definitive resource for all supported fields. I saw mention of <code>image_width</code> in a help text so that’s one, and you can probably land on the one you want by guesstimating.</p>"
    },
    {
      "id": "https://msfjarvis.dev/notes/til-the-pac-man-rule-at-conferences/",
      "url": "https://msfjarvis.dev/notes/til-the-pac-man-rule-at-conferences/",
      "title": "TIL: The Pac-Man Rule at Conferences",
      "date_published": "2026-05-16T06:54:00.954Z",
      "summary": "A simple change to make conferences much less intimidating to new participants",
      "content_html": "<p>I came across a Fediverse <a href=\"https://fosstodon.org/@ericholscher/116579642732337477\">post</a> today mentioning the Pac-Man rule being encouraged at PyCon and was pleasantly surprised by how such a small change can alter the dynamic of being in an intimidating conference setting.</p>\n<p>Their <a href=\"https://www.ericholscher.com/blog/2017/aug/2/pacman-rule-conferences/\">linked blog post</a> lays it out pretty succinctly, but the core premise is just being slightly more intentional about how groups present themselves to other attendees. By leaving a gap in circle of people talking to each other, quite like Pac-Man’s mouth, you change a closed group into one that invites people to join. So clever!</p>"
    },
    {
      "id": "https://msfjarvis.dev/notes/til-customizing-the-koreader-home-page/",
      "url": "https://msfjarvis.dev/notes/til-customizing-the-koreader-home-page/",
      "title": "TIL: Customizing the KOReader home page",
      "date_published": "2026-04-24T14:30:00.000Z",
      "summary": "A thousand thanks to Yash who will now be the only reason I really use KOReader",
      "content_html": "<p>I’ve complained many a times about how I find <a href=\"https://koreader.rocks\">KOReader</a> to be incredibly feature-full but quite difficult on the eyes, which resulted in me only using it to access <a href=\"https://miniflux.app\">Miniflux</a> on occasion and sticking to the regular Kobo software for reading my books. I was grumbling about that to <a href=\"https://yashgarg.dev%5D\">Yash</a> today who told me of the <a href=\"https://github.com/doctorhetfield-cmd/simpleui.koplugin\">SimpleUI</a> plugin and the absolute smorgasbord of <a href=\"https://www.reddit.com/r/koreader/comments/1sseipx/setup_mega_thread/\">incredibly beautiful setups</a> people have created with it.</p>\n<p>I’ve installed the plugin today, I will be spending the weekend being entirely too enamored by it and <a href=\"https://itsfoss.community/t/what-the-hell-is-the-definition-of-ricing-in-linux/14160\">ricing</a> it to extreme levels.</p>"
    },
    {
      "id": "https://msfjarvis.dev/notes/til-sending-emails-from-my-duckduckgo-email-alias/",
      "url": "https://msfjarvis.dev/notes/til-sending-emails-from-my-duckduckgo-email-alias/",
      "title": "TIL: Sending emails from my DuckDuckGo email alias",
      "date_published": "2026-02-23T12:20:00.000Z",
      "summary": "Slightly regretting using one of these for a credit card 😬",
      "content_html": "<p>I use <a href=\"https://duckduckgo.com/email/\">DuckDuckGo Email Protection</a> for my accounts in a lot of places, one of which happened to be a credit card company. I needed to file a support ticket for it and, rightfully so, they require the sender to be the original email that I signed up with. I was expecting this to be a bit of a nightmare but thankfully DuckDuckGo already thought of this and have a rather simple way to do this.</p>\n<p>If your alias is <code>john.doe@duck.com</code> and you wish to send an email to <code>jane.doe@example.com</code>, you can do so by sending an email from your forwarding address to <code>jane.doe_at_example.com_john.doe@duck.com</code>. What we did here is replace the <code>@</code> in the receiver address with <code>_at_</code> and then append our alias, again separated with a <code>_</code>. This lets this continue being a valid email while being unambiguous for DuckDuckGo’s routing infrastructure. I tried it out and immediately got the confirmation that my support ticket had been accepted! After the initial hurdle replying normally will continue to work.</p>"
    },
    {
      "id": "https://msfjarvis.dev/notes/til-easily-sending-ebooks-to-a-kobo/",
      "url": "https://msfjarvis.dev/notes/til-easily-sending-ebooks-to-a-kobo/",
      "title": "TIL: Easily sending eBooks to a Kobo",
      "date_published": "2026-02-06T06:03:00.000Z",
      "summary": "send.djazz.se is a godsend (no pun intended)",
      "content_html": "<p>Discovered <a href=\"https://send.djazz.se\">send.djazz.se</a> via a Reddit comment that greatly simplifies sending eBooks to a Kobo in the absence of a computer to run Calibre on.</p>\n<p>Simply go to the website on the Kobo, memorize the 4 digit code it generates then go to the same website on your other device, enter the code and upload your file. Within a few seconds it should show up on the Kobo as a downloadable link, and it has the additional options to convert your eBook into a native format for the target device. Super convenient!</p>"
    },
    {
      "id": "https://msfjarvis.dev/notes/android-gradle-plugin-9.0.0-upgrade-notes/",
      "url": "https://msfjarvis.dev/notes/android-gradle-plugin-9.0.0-upgrade-notes/",
      "title": "Android Gradle Plugin 9.0.0 upgrade notes",
      "date_published": "2026-01-13T01:14:00.000Z",
      "summary": "Some tips to ease the Android Gradle Plugin 9.0.0 upgrade",
      "content_html": "<p>Version 9.0.0 of the Android Gradle Plugin brings in <a href=\"https://developer.android.com/build/releases/agp-preview#android-gradle-plugin\">a lot of breaking changes</a>, most of which are documented with clear upgrade paths except the fact that Kotlin Multiplatform libraries now ship their Android artifacts <a href=\"https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-main:build-system/gradle-api/src/main/java/com/android/build/api/dsl/KmpOptimization.kt;l=79-87;drc=04e1247ed3cc69320f919ebb46b763e31483a7a5\">without consumer keep rules included</a>. This needs to be manually turned on to preserve the existing behavior:</p>\n<div class=\"expressive-code\"><link rel=\"stylesheet\" href=\"https://msfjarvis.dev/_astro/ec.cteup.css\"><figure class=\"frame\"><figcaption class=\"header\"></figcaption><pre data-language=\"kotlin\"><code><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#1A57D1;--0fs:italic;--1:#FABD2F\">android</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> {</span></div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\">  </span><span style=\"--0:#1A57D1;--0fs:italic;--1:#FABD2F\">consumerKeepRules</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> {</span></div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\"><span style=\"--0:#4C4F69;--1:#EBDBB2\">    </span></span><span style=\"--0:#4C4F69;--1:#EBDBB2\">publish </span><span style=\"--0:#116B70;--1:#8EC07C\">=</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#A54107;--1:#D995A8\">true</span></div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\">    </span><span style=\"--0:#1A57D1;--0fs:italic;--1:#FABD2F\">file</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">(</span><span style=\"--0:#2C6E1E;--1:#B8BB26\">\"consumer-proguard-rules.pro\"</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">)</span></div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\"><span style=\"--0:#4C4F69;--1:#EBDBB2\">  </span></span><span style=\"--0:#4C4F69;--1:#EBDBB2\">}</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4C4F69;--1:#EBDBB2\">}</span></div></div></code></pre><div class=\"copy\"><div aria-live=\"polite\"></div><button title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"android {  consumerKeepRules {    publish = true    file(&quot;consumer-proguard-rules.pro&quot;)  }}\"><div></div></button></div></figure></div>"
    },
    {
      "id": "https://msfjarvis.dev/notes/collating-entries-in-my-obsidian-journal-for-week-notes/",
      "url": "https://msfjarvis.dev/notes/collating-entries-in-my-obsidian-journal-for-week-notes/",
      "title": "Collating entries in my Obsidian journal for week notes",
      "date_published": "2025-12-14T15:51:00.000Z",
      "summary": "Quick how-to of setting up my Obsidian journal to create a gist of noteworthy events through the week",
      "content_html": "<p>Following up on my <a href=\"https://masto-archive.msfjarvis.dev/@msfjarvis/115717823447859331\">Mastodon</a> post about wanting to start doing week notes, inspired by <a href=\"https://ankursethi.com/\">Ankur</a> and <a href=\"https://abhinavsarkar.net/notes/tags/weeknotes/\">Abhinav</a>, here’s the quick rundown of how I supplemented my existing Obsidian journal to facilitate the new endeavor.</p>\n<p>I currently use <a href=\"https://github.com/liamcain/obsidian-periodic-notes\">obsidian-periodic-notes</a> to manage my daily notes and have mostly left the weekly/monthly option untouched, until now.</p>\n<p>To actually collect the necessary journal entries, I relied on <a href=\"https://github.com/blacksmithgu/obsidian-dataview\">Dataview</a> which I’ve <a href=\"https://msfjarvis.dev/posts/overengineering-an-obsidian-dashboard-to-get-better-at-marvel-snap/\">used previously to get good at Marvel Snap</a>. I wanted to minimize the changes I needed to make in my day-to-day workflow so I started with the restriction that at most I would be willing to tack on <code>#weekly</code> at the end of a journal entry that was noteworthy.</p>\n<p>Dataview was able to beautifully navigate this limitation and with a little bit of JavaScript I now have a template that I can plug into Periodic Notes and get a roundup of the whole week in one click. Here’s how it looks with just today’s entries highlighted:</p>\n<figure data-image-modal-root=\"\" data-astro-cid-xekddyko=\"\"><a class=\"pswp-gallery-item image-modal__trigger\" href=\"https://msfjarvis.dev/_astro/obsidian-weeknotes.DAjjP-s-.webp\" data-pswp-light-src=\"https://msfjarvis.dev/_astro/obsidian-weeknotes.DAjjP-s-.webp\" data-pswp-width=\"775\" data-pswp-height=\"325\" data-pswp-caption=\"Week 50 roundup with entries from Sunday\" aria-label=\"An Obsidian page with the title 2025-W50 and listing two journal entries from today: Me writing about my Droidcon India 2025 experience and a mention of me finishing my second run of the game Dispatch.\" data-astro-cid-xekddyko=\"\"><span class=\"image-modal__media\" data-astro-cid-xekddyko=\"\"><picture data-astro-cid-xekddyko=\"\"><img src=\"https://msfjarvis.dev/_astro/obsidian-weeknotes.DAjjP-s-.webp\" alt=\"An Obsidian page with the title 2025-W50 and listing two journal entries from today: Me writing about my Droidcon India 2025 experience and a mention of me finishing my second run of the game Dispatch.\" loading=\"lazy\" decoding=\"async\" width=\"775\" height=\"325\" data-astro-cid-xekddyko=\"\"></picture><span class=\"image-modal__hint\" aria-hidden=\"true\" data-astro-cid-xekddyko=\"\">Expand image</span></span></a><figcaption data-astro-cid-xekddyko=\"\">Week 50 roundup with entries from Sunday</figcaption></figure>\n<p>The relevant DataViewJS code is this:</p>\n<div class=\"expressive-code\"><link rel=\"stylesheet\" href=\"https://msfjarvis.dev/_astro/ec.cteup.css\"><figure class=\"frame\"><figcaption class=\"header\"></figcaption><pre data-language=\"javascript\"><code><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4C4F69;--1:#EBDBB2\">(</span><span style=\"--0:#5D5F6E;--1:#B4A694\">()</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#7B34D9;--1:#FE8929\">=&gt;</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#5D5F6E;--1:#B4A694\">{</span></div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\">  </span><span style=\"--0:#7B34D9;--1:#FE8929\">const</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#4C4F69;--1:#91AFA3\">TAG</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#116B70;--1:#8EC07C\">=</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#2C6E1E;--1:#B4A694\">\"</span><span style=\"--0:#2C6E1E;--1:#B8BB26\">#weekly</span><span style=\"--0:#2C6E1E;--1:#B4A694\">\"</span><span style=\"--0:#5D5F6E;--1:#B4A694\">;</span></div></div><div class=\"ec-line\"><div class=\"code\">\n</div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\">  </span><span style=\"--0:#7B34D9;--1:#FE8929\">const</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#4C4F69;--1:#91AFA3\">m</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#116B70;--1:#8EC07C\">=</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#4C4F69;--1:#91AFA3\">dv</span><span style=\"--0:#116B70;--1:#B4A694\">.</span><span style=\"--0:#1A57D1;--0fs:italic;--1:#FABD2F\">current</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">()</span><span style=\"--0:#116B70;--1:#B4A694\">.</span><span style=\"--0:#4C4F69;--1:#91AFA3\">file</span><span style=\"--0:#116B70;--1:#B4A694\">.</span><span style=\"--0:#4C4F69;--1:#91AFA3\">name</span><span style=\"--0:#116B70;--1:#B4A694\">.</span><span style=\"--0:#1A57D1;--0fs:italic;--1:#FABD2F\">match</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">(</span><span style=\"--0:#8D477A;--1:#B4A694\">/</span><span style=\"--0:#7B34D9;--1:#FC8678\">^</span><span style=\"--0:#2C6E1E;--1:#B4A694\">(</span><span style=\"--0:#8D477A;--1:#D995A8\">\\d</span><span style=\"--0:#116B70;--1:#8EC07C\">{4}</span><span style=\"--0:#2C6E1E;--1:#B4A694\">)</span><span style=\"--0:#2C6E1E;--1:#FE8929\">-W</span><span style=\"--0:#2C6E1E;--1:#B4A694\">(</span><span style=\"--0:#8D477A;--1:#D995A8\">\\d</span><span style=\"--0:#116B70;--1:#8EC07C\">{2}</span><span style=\"--0:#2C6E1E;--1:#B4A694\">)</span><span style=\"--0:#7B34D9;--1:#FC8678\">$</span><span style=\"--0:#8D477A;--1:#B4A694\">/</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">)</span><span style=\"--0:#5D5F6E;--1:#B4A694\">;</span></div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\">  </span><span style=\"--0:#7B34D9;--1:#FC8678\">if</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> (</span><span style=\"--0:#116B70;--1:#FC8678\">!</span><span style=\"--0:#4C4F69;--1:#91AFA3\">m</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">) </span><span style=\"--0:#5D5F6E;--1:#B4A694\">{</span></div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\"><span style=\"--0:#4C4F69\">    </span></span><span style=\"--0:#4C4F69;--1:#91AFA3\">dv</span><span style=\"--0:#116B70;--1:#B4A694\">.</span><span style=\"--0:#1A57D1;--0fs:italic;--1:#FABD2F\">paragraph</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">(</span><span style=\"--0:#2C6E1E;--1:#B4A694\">\"</span><span style=\"--0:#2C6E1E;--1:#B8BB26\">Weekly note filename must be like `2025-W50.md`.</span><span style=\"--0:#2C6E1E;--1:#B4A694\">\"</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">)</span><span style=\"--0:#5D5F6E;--1:#B4A694\">;</span></div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\">    </span><span style=\"--0:#7B34D9;--1:#FC8678\">return</span><span style=\"--0:#5D5F6E;--1:#B4A694\">;</span></div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\">  </span><span style=\"--0:#5D5F6E;--1:#B4A694\">}</span></div></div><div class=\"ec-line\"><div class=\"code\">\n</div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\">  </span><span style=\"--0:#7B34D9;--1:#FE8929\">function</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#1A57D1;--0fs:italic;--1:#FABD2F\">isoWeekStartISO</span><span style=\"--0:#5D5F6E;--1:#B4A694\">(</span><span style=\"--0:#AD343E;--0fs:italic;--1:#91AFA3\">year</span><span style=\"--0:#5D5F6E;--1:#B4A694\">,</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#AD343E;--0fs:italic;--1:#91AFA3\">week</span><span style=\"--0:#5D5F6E;--1:#B4A694\">)</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#5D5F6E;--1:#B4A694\">{</span></div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\">    </span><span style=\"--0:#5D5F6E;--0fs:italic;--1:#B1A69C;--1fs:italic\">// Compute ISO week start (Monday) in UTC, then return YYYY-MM-DD</span></div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\">    </span><span style=\"--0:#7B34D9;--1:#FE8929\">const</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#4C4F69;--1:#91AFA3\">simple</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#116B70;--1:#8EC07C\">=</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#7B34D9;--0fw:bold;--1:#FE8929\">new</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#1A57D1;--0fs:italic;--1:#FABD2F\">Date</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">(</span><span style=\"--0:#4C4F69;--1:#91AFA3\">Date</span><span style=\"--0:#116B70;--1:#B4A694\">.</span><span style=\"--0:#1A57D1;--0fs:italic;--1:#FABD2F\">UTC</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">(</span><span style=\"--0:#4C4F69;--1:#91AFA3\">year</span><span style=\"--0:#5D5F6E;--1:#B4A694\">,</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#A54107;--1:#D995A8\">0</span><span style=\"--0:#5D5F6E;--1:#B4A694\">,</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#A54107;--1:#D995A8\">1</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#116B70;--1:#8EC07C\">+</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> (</span><span style=\"--0:#4C4F69;--1:#91AFA3\">week</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#116B70;--1:#8EC07C\">-</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#A54107;--1:#D995A8\">1</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">) </span><span style=\"--0:#116B70;--1:#8EC07C\">*</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#A54107;--1:#D995A8\">7</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">))</span><span style=\"--0:#5D5F6E;--1:#B4A694\">;</span></div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\">    </span><span style=\"--0:#7B34D9;--1:#FE8929\">const</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#4C4F69;--1:#91AFA3\">dow</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#116B70;--1:#8EC07C\">=</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#4C4F69;--1:#91AFA3\">simple</span><span style=\"--0:#116B70;--1:#B4A694\">.</span><span style=\"--0:#1A57D1;--0fs:italic;--1:#FABD2F\">getUTCDay</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">()</span><span style=\"--0:#5D5F6E;--1:#B4A694\">;</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#5D5F6E;--0fs:italic;--1:#B1A69C;--1fs:italic\">// 0..6, Sun..Sat</span></div></div><div class=\"ec-line\"><div class=\"code\">\n</div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\">    </span><span style=\"--0:#7B34D9;--1:#FE8929\">const</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#4C4F69;--1:#91AFA3\">start</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#116B70;--1:#8EC07C\">=</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#7B34D9;--0fw:bold;--1:#FE8929\">new</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#1A57D1;--0fs:italic;--1:#FABD2F\">Date</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">(</span><span style=\"--0:#4C4F69;--1:#91AFA3\">simple</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">)</span><span style=\"--0:#5D5F6E;--1:#B4A694\">;</span></div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\">    </span><span style=\"--0:#7B34D9;--1:#FC8678\">if</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> (</span><span style=\"--0:#4C4F69;--1:#91AFA3\">dow</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#116B70;--1:#8EC07C\">&lt;=</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#A54107;--1:#D995A8\">4</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">) </span><span style=\"--0:#4C4F69;--1:#91AFA3\">start</span><span style=\"--0:#116B70;--1:#B4A694\">.</span><span style=\"--0:#1A57D1;--0fs:italic;--1:#FABD2F\">setUTCDate</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">(</span><span style=\"--0:#4C4F69;--1:#91AFA3\">simple</span><span style=\"--0:#116B70;--1:#B4A694\">.</span><span style=\"--0:#1A57D1;--0fs:italic;--1:#FABD2F\">getUTCDate</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">() </span><span style=\"--0:#116B70;--1:#8EC07C\">-</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#4C4F69;--1:#91AFA3\">dow</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#116B70;--1:#8EC07C\">+</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#A54107;--1:#D995A8\">1</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">)</span><span style=\"--0:#5D5F6E;--1:#B4A694\">;</span></div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\">    </span><span style=\"--0:#7B34D9;--1:#FC8678\">else</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#4C4F69;--1:#91AFA3\">start</span><span style=\"--0:#116B70;--1:#B4A694\">.</span><span style=\"--0:#1A57D1;--0fs:italic;--1:#FABD2F\">setUTCDate</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">(</span><span style=\"--0:#4C4F69;--1:#91AFA3\">simple</span><span style=\"--0:#116B70;--1:#B4A694\">.</span><span style=\"--0:#1A57D1;--0fs:italic;--1:#FABD2F\">getUTCDate</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">() </span><span style=\"--0:#116B70;--1:#8EC07C\">+</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> (</span><span style=\"--0:#A54107;--1:#D995A8\">8</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#116B70;--1:#8EC07C\">-</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#4C4F69;--1:#91AFA3\">dow</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">))</span><span style=\"--0:#5D5F6E;--1:#B4A694\">;</span></div></div><div class=\"ec-line\"><div class=\"code\">\n</div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\">    </span><span style=\"--0:#5D5F6E;--0fs:italic;--1:#B1A69C;--1fs:italic\">// YYYY-MM-DD</span></div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\">    </span><span style=\"--0:#7B34D9;--1:#FC8678\">return</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#4C4F69;--1:#91AFA3\">start</span><span style=\"--0:#116B70;--1:#B4A694\">.</span><span style=\"--0:#1A57D1;--0fs:italic;--1:#FABD2F\">toISOString</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">()</span><span style=\"--0:#116B70;--1:#B4A694\">.</span><span style=\"--0:#1A57D1;--0fs:italic;--1:#FABD2F\">slice</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">(</span><span style=\"--0:#A54107;--1:#D995A8\">0</span><span style=\"--0:#5D5F6E;--1:#B4A694\">,</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#A54107;--1:#D995A8\">10</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">)</span><span style=\"--0:#5D5F6E;--1:#B4A694\">;</span></div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\">  </span><span style=\"--0:#5D5F6E;--1:#B4A694\">}</span></div></div><div class=\"ec-line\"><div class=\"code\">\n</div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\">  </span><span style=\"--0:#7B34D9;--1:#FE8929\">const</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#4C4F69;--1:#91AFA3\">year</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#116B70;--1:#8EC07C\">=</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#1A57D1;--0fs:italic;--1:#FABD2F\">Number</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">(</span><span style=\"--0:#4C4F69;--1:#91AFA3\">m</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">[</span><span style=\"--0:#A54107;--1:#D995A8\">1</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">])</span><span style=\"--0:#5D5F6E;--1:#B4A694\">;</span></div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\">  </span><span style=\"--0:#7B34D9;--1:#FE8929\">const</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#4C4F69;--1:#91AFA3\">week</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#116B70;--1:#8EC07C\">=</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#1A57D1;--0fs:italic;--1:#FABD2F\">Number</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">(</span><span style=\"--0:#4C4F69;--1:#91AFA3\">m</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">[</span><span style=\"--0:#A54107;--1:#D995A8\">2</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">])</span><span style=\"--0:#5D5F6E;--1:#B4A694\">;</span></div></div><div class=\"ec-line\"><div class=\"code\">\n</div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\">  </span><span style=\"--0:#7B34D9;--1:#FE8929\">const</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#4C4F69;--1:#91AFA3\">start</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#116B70;--1:#8EC07C\">=</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#4C4F69;--1:#91AFA3\">dv</span><span style=\"--0:#116B70;--1:#B4A694\">.</span><span style=\"--0:#1A57D1;--0fs:italic;--1:#FABD2F\">date</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">(</span><span style=\"--0:#1A57D1;--0fs:italic;--1:#FABD2F\">isoWeekStartISO</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">(</span><span style=\"--0:#4C4F69;--1:#91AFA3\">year</span><span style=\"--0:#5D5F6E;--1:#B4A694\">,</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#4C4F69;--1:#91AFA3\">week</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">))</span><span style=\"--0:#5D5F6E;--1:#B4A694\">;</span></div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\">  </span><span style=\"--0:#7B34D9;--1:#FE8929\">const</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#4C4F69;--1:#91AFA3\">end</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#116B70;--1:#8EC07C\">=</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#4C4F69;--1:#91AFA3\">start</span><span style=\"--0:#116B70;--1:#B4A694\">.</span><span style=\"--0:#1A57D1;--0fs:italic;--1:#FABD2F\">plus</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">(</span><span style=\"--0:#5D5F6E;--1:#B4A694\">{</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> days</span><span style=\"--0:#116B70;--1:#B4A694\">:</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#A54107;--1:#D995A8\">7</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#5D5F6E;--1:#B4A694\">}</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">)</span><span style=\"--0:#5D5F6E;--1:#B4A694\">;</span></div></div><div class=\"ec-line\"><div class=\"code\">\n</div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\">  </span><span style=\"--0:#7B34D9;--1:#FE8929\">const</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#4C4F69;--1:#91AFA3\">pages</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#116B70;--1:#8EC07C\">=</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#4C4F69;--1:#91AFA3\">dv</span></div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\">    </span><span style=\"--0:#116B70;--1:#B4A694\">.</span><span style=\"--0:#1A57D1;--0fs:italic;--1:#FABD2F\">pages</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">(</span><span style=\"--0:#2C6E1E;--1:#B4A694\">'</span><span style=\"--0:#2C6E1E;--1:#B8BB26\">\"Daily\"</span><span style=\"--0:#2C6E1E;--1:#B4A694\">'</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">)</span></div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\">    </span><span style=\"--0:#116B70;--1:#B4A694\">.</span><span style=\"--0:#1A57D1;--0fs:italic;--1:#FABD2F\">where</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">(</span><span style=\"--0:#5D5F6E;--1:#B4A694\">(</span><span style=\"--0:#AD343E;--0fs:italic;--1:#91AFA3\">p</span><span style=\"--0:#5D5F6E;--1:#B4A694\">)</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#7B34D9;--1:#FE8929\">=&gt;</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#4C4F69;--1:#91AFA3\">p</span><span style=\"--0:#116B70;--1:#B4A694\">.</span><span style=\"--0:#4C4F69;--1:#91AFA3\">file</span><span style=\"--0:#116B70;--1:#B4A694\">.</span><span style=\"--0:#4C4F69;--1:#91AFA3\">day</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#116B70;--1:#FC8678\">&amp;&amp;</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#4C4F69;--1:#91AFA3\">p</span><span style=\"--0:#116B70;--1:#B4A694\">.</span><span style=\"--0:#4C4F69;--1:#91AFA3\">file</span><span style=\"--0:#116B70;--1:#B4A694\">.</span><span style=\"--0:#4C4F69;--1:#91AFA3\">day</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#116B70;--1:#8EC07C\">&gt;=</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#4C4F69;--1:#91AFA3\">start</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#116B70;--1:#FC8678\">&amp;&amp;</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#4C4F69;--1:#91AFA3\">p</span><span style=\"--0:#116B70;--1:#B4A694\">.</span><span style=\"--0:#4C4F69;--1:#91AFA3\">file</span><span style=\"--0:#116B70;--1:#B4A694\">.</span><span style=\"--0:#4C4F69;--1:#91AFA3\">day</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#116B70;--1:#8EC07C\">&lt;</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#4C4F69;--1:#91AFA3\">end</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">)</span></div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\">    </span><span style=\"--0:#116B70;--1:#B4A694\">.</span><span style=\"--0:#1A57D1;--0fs:italic;--1:#FABD2F\">sort</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">(</span><span style=\"--0:#5D5F6E;--1:#B4A694\">(</span><span style=\"--0:#AD343E;--0fs:italic;--1:#91AFA3\">p</span><span style=\"--0:#5D5F6E;--1:#B4A694\">)</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#7B34D9;--1:#FE8929\">=&gt;</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#4C4F69;--1:#91AFA3\">p</span><span style=\"--0:#116B70;--1:#B4A694\">.</span><span style=\"--0:#4C4F69;--1:#91AFA3\">file</span><span style=\"--0:#116B70;--1:#B4A694\">.</span><span style=\"--0:#4C4F69;--1:#91AFA3\">day</span><span style=\"--0:#5D5F6E;--1:#B4A694\">,</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#2C6E1E;--1:#B4A694\">\"</span><span style=\"--0:#2C6E1E;--1:#B8BB26\">asc</span><span style=\"--0:#2C6E1E;--1:#B4A694\">\"</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">)</span><span style=\"--0:#5D5F6E;--1:#B4A694\">;</span></div></div><div class=\"ec-line\"><div class=\"code\">\n</div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\">  </span><span style=\"--0:#7B34D9;--1:#FE8929\">let</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#4C4F69;--1:#91AFA3\">any</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#116B70;--1:#8EC07C\">=</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#A54107;--1:#D995A8\">false</span><span style=\"--0:#5D5F6E;--1:#B4A694\">;</span></div></div><div class=\"ec-line\"><div class=\"code\">\n</div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\">  </span><span style=\"--0:#7B34D9;--1:#FC8678\">for</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> (</span><span style=\"--0:#7B34D9;--1:#FE8929\">const</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#4C4F69;--1:#91AFA3\">p</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#7B34D9;--1:#8EC07C\">of</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#4C4F69;--1:#91AFA3\">pages</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">) </span><span style=\"--0:#5D5F6E;--1:#B4A694\">{</span></div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\">    </span><span style=\"--0:#7B34D9;--1:#FE8929\">const</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#4C4F69;--1:#91AFA3\">bullets</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#116B70;--1:#8EC07C\">=</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> (</span><span style=\"--0:#4C4F69;--1:#91AFA3\">p</span><span style=\"--0:#116B70;--1:#B4A694\">.</span><span style=\"--0:#4C4F69;--1:#91AFA3\">file</span><span style=\"--0:#116B70;--1:#B4A694\">.</span><span style=\"--0:#4C4F69;--1:#91AFA3\">lists</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#116B70;--1:#FC8678\">??</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> [])</span><span style=\"--0:#116B70;--1:#B4A694\">.</span><span style=\"--0:#1A57D1;--0fs:italic;--1:#FABD2F\">filter</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">(</span><span style=\"--0:#5D5F6E;--1:#B4A694\">(</span><span style=\"--0:#AD343E;--0fs:italic;--1:#91AFA3\">li</span><span style=\"--0:#5D5F6E;--1:#B4A694\">)</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#7B34D9;--1:#FE8929\">=&gt;</span></div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\"><span style=\"--0:#4C4F69;--1:#EBDBB2\">      </span></span><span style=\"--0:#4C4F69;--1:#EBDBB2\">(</span><span style=\"--0:#4C4F69;--1:#91AFA3\">li</span><span style=\"--0:#116B70;--1:#B4A694\">.</span><span style=\"--0:#4C4F69;--1:#91AFA3\">text</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#116B70;--1:#FC8678\">??</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#2C6E1E;--1:#B4A694\">\"\"</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">)</span><span style=\"--0:#116B70;--1:#B4A694\">.</span><span style=\"--0:#1A57D1;--0fs:italic;--1:#FABD2F\">includes</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">(</span><span style=\"--0:#4C4F69;--1:#91AFA3\">TAG</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">)</span><span style=\"--0:#5D5F6E;--1:#B4A694\">,</span></div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\"><span style=\"--0:#4C4F69;--1:#EBDBB2\">    </span></span><span style=\"--0:#4C4F69;--1:#EBDBB2\">)</span><span style=\"--0:#5D5F6E;--1:#B4A694\">;</span></div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\">    </span><span style=\"--0:#7B34D9;--1:#FC8678\">if</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> (</span><span style=\"--0:#116B70;--1:#FC8678\">!</span><span style=\"--0:#4C4F69;--1:#91AFA3\">bullets</span><span style=\"--0:#116B70;--1:#B4A694\">.</span><span style=\"--0:#4C4F69;--1:#D995A8\">length</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">) </span><span style=\"--0:#7B34D9;--1:#FC8678\">continue</span><span style=\"--0:#5D5F6E;--1:#B4A694\">;</span></div></div><div class=\"ec-line\"><div class=\"code\">\n</div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\"><span style=\"--0:#4C4F69\">    </span></span><span style=\"--0:#4C4F69;--1:#91AFA3\">any</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#116B70;--1:#8EC07C\">=</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#A54107;--1:#D995A8\">true</span><span style=\"--0:#5D5F6E;--1:#B4A694\">;</span></div></div><div class=\"ec-line\"><div class=\"code\">\n</div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\">    </span><span style=\"--0:#5D5F6E;--0fs:italic;--1:#B1A69C;--1fs:italic\">// [[YYYY-MM-DD]]</span></div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\"><span style=\"--0:#4C4F69\">    </span></span><span style=\"--0:#4C4F69;--1:#91AFA3\">dv</span><span style=\"--0:#116B70;--1:#B4A694\">.</span><span style=\"--0:#1A57D1;--0fs:italic;--1:#FABD2F\">paragraph</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">(</span><span style=\"--0:#4C4F69;--1:#91AFA3\">p</span><span style=\"--0:#116B70;--1:#B4A694\">.</span><span style=\"--0:#4C4F69;--1:#91AFA3\">file</span><span style=\"--0:#116B70;--1:#B4A694\">.</span><span style=\"--0:#4C4F69;--1:#91AFA3\">link</span><span style=\"--0:#116B70;--1:#B4A694\">.</span><span style=\"--0:#1A57D1;--0fs:italic;--1:#FABD2F\">toString</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">())</span><span style=\"--0:#5D5F6E;--1:#B4A694\">;</span></div></div><div class=\"ec-line\"><div class=\"code\">\n</div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\">    </span><span style=\"--0:#5D5F6E;--0fs:italic;--1:#B1A69C;--1fs:italic\">// - Line 1</span></div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\">    </span><span style=\"--0:#5D5F6E;--0fs:italic;--1:#B1A69C;--1fs:italic\">// - Line 2</span></div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\"><span style=\"--0:#4C4F69\">    </span></span><span style=\"--0:#4C4F69;--1:#91AFA3\">dv</span><span style=\"--0:#116B70;--1:#B4A694\">.</span><span style=\"--0:#1A57D1;--0fs:italic;--1:#FABD2F\">list</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">(</span></div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\"><span style=\"--0:#4C4F69\">      </span></span><span style=\"--0:#4C4F69;--1:#91AFA3\">bullets</span></div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\">        </span><span style=\"--0:#116B70;--1:#B4A694\">.</span><span style=\"--0:#1A57D1;--0fs:italic;--1:#FABD2F\">map</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">(</span><span style=\"--0:#5D5F6E;--1:#B4A694\">(</span><span style=\"--0:#AD343E;--0fs:italic;--1:#91AFA3\">li</span><span style=\"--0:#5D5F6E;--1:#B4A694\">)</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#7B34D9;--1:#FE8929\">=&gt;</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#4C4F69;--1:#91AFA3\">li</span><span style=\"--0:#116B70;--1:#B4A694\">.</span><span style=\"--0:#4C4F69;--1:#91AFA3\">text</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#116B70;--1:#FC8678\">??</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#2C6E1E;--1:#B4A694\">\"\"</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">)</span></div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\">        </span><span style=\"--0:#116B70;--1:#B4A694\">.</span><span style=\"--0:#1A57D1;--0fs:italic;--1:#FABD2F\">map</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">(</span><span style=\"--0:#5D5F6E;--1:#B4A694\">(</span><span style=\"--0:#AD343E;--0fs:italic;--1:#91AFA3\">t</span><span style=\"--0:#5D5F6E;--1:#B4A694\">)</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#7B34D9;--1:#FE8929\">=&gt;</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#4C4F69;--1:#91AFA3\">t</span><span style=\"--0:#116B70;--1:#B4A694\">.</span><span style=\"--0:#1A57D1;--0fs:italic;--1:#FABD2F\">replace</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">(</span><span style=\"--0:#4C4F69;--1:#91AFA3\">TAG</span><span style=\"--0:#5D5F6E;--1:#B4A694\">,</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#2C6E1E;--1:#B4A694\">\"\"</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">)</span><span style=\"--0:#116B70;--1:#B4A694\">.</span><span style=\"--0:#1A57D1;--0fs:italic;--1:#FABD2F\">replace</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">(</span><span style=\"--0:#8D477A;--1:#B4A694\">/</span><span style=\"--0:#8D477A;--1:#D995A8\">\\s</span><span style=\"--0:#116B70;--1:#8EC07C\">+</span><span style=\"--0:#8D477A;--1:#B4A694\">/</span><span style=\"--0:#7B34D9;--1:#FC8678\">g</span><span style=\"--0:#5D5F6E;--1:#B4A694\">,</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> </span><span style=\"--0:#2C6E1E;--1:#B4A694\">\"</span><span style=\"--0:#2C6E1E;--1:#B8BB26\"> </span><span style=\"--0:#2C6E1E;--1:#B4A694\">\"</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">)</span><span style=\"--0:#116B70;--1:#B4A694\">.</span><span style=\"--0:#1A57D1;--0fs:italic;--1:#FABD2F\">trim</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">())</span></div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\">        </span><span style=\"--0:#116B70;--1:#B4A694\">.</span><span style=\"--0:#1A57D1;--0fs:italic;--1:#FABD2F\">filter</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">(</span><span style=\"--0:#4C4F69;--1:#91AFA3\">Boolean</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">)</span><span style=\"--0:#5D5F6E;--1:#B4A694\">,</span></div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\"><span style=\"--0:#4C4F69;--1:#EBDBB2\">    </span></span><span style=\"--0:#4C4F69;--1:#EBDBB2\">)</span><span style=\"--0:#5D5F6E;--1:#B4A694\">;</span></div></div><div class=\"ec-line\"><div class=\"code\">\n</div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\"><span style=\"--0:#4C4F69\">    </span></span><span style=\"--0:#4C4F69;--1:#91AFA3\">dv</span><span style=\"--0:#116B70;--1:#B4A694\">.</span><span style=\"--0:#1A57D1;--0fs:italic;--1:#FABD2F\">paragraph</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">(</span><span style=\"--0:#2C6E1E;--1:#B4A694\">\"\"</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">)</span><span style=\"--0:#5D5F6E;--1:#B4A694\">;</span></div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\">  </span><span style=\"--0:#5D5F6E;--1:#B4A694\">}</span></div></div><div class=\"ec-line\"><div class=\"code\">\n</div></div><div class=\"ec-line\"><div class=\"code\"><span class=\"indent\">  </span><span style=\"--0:#7B34D9;--1:#FC8678\">if</span><span style=\"--0:#4C4F69;--1:#EBDBB2\"> (</span><span style=\"--0:#116B70;--1:#FC8678\">!</span><span style=\"--0:#4C4F69;--1:#91AFA3\">any</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">) </span><span style=\"--0:#4C4F69;--1:#91AFA3\">dv</span><span style=\"--0:#116B70;--1:#B4A694\">.</span><span style=\"--0:#1A57D1;--0fs:italic;--1:#FABD2F\">paragraph</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">(</span><span style=\"--0:#2C6E1E;--1:#B4A694\">`</span><span style=\"--0:#2C6E1E;--1:#B8BB26\">No </span><span style=\"--0:#5D5F6E;--1:#B4A694\">${</span><span style=\"--0:#4C4F69;--1:#91AFA3\">TAG</span><span style=\"--0:#5D5F6E;--1:#B4A694\">}</span><span style=\"--0:#2C6E1E;--1:#B8BB26\"> bullets found for </span><span style=\"--0:#5D5F6E;--1:#B4A694\">${</span><span style=\"--0:#4C4F69;--1:#91AFA3\">m</span><span style=\"--0:#2C6E1E;--1:#B8BB26\">[</span><span style=\"--0:#A54107;--1:#D995A8\">1</span><span style=\"--0:#2C6E1E;--1:#B8BB26\">]</span><span style=\"--0:#5D5F6E;--1:#B4A694\">}</span><span style=\"--0:#2C6E1E;--1:#B8BB26\">-W</span><span style=\"--0:#5D5F6E;--1:#B4A694\">${</span><span style=\"--0:#4C4F69;--1:#91AFA3\">m</span><span style=\"--0:#2C6E1E;--1:#B8BB26\">[</span><span style=\"--0:#A54107;--1:#D995A8\">2</span><span style=\"--0:#2C6E1E;--1:#B8BB26\">]</span><span style=\"--0:#5D5F6E;--1:#B4A694\">}</span><span style=\"--0:#2C6E1E;--1:#B8BB26\">.</span><span style=\"--0:#2C6E1E;--1:#B4A694\">`</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">)</span><span style=\"--0:#5D5F6E;--1:#B4A694\">;</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#5D5F6E;--1:#B4A694\">}</span><span style=\"--0:#4C4F69;--1:#EBDBB2\">)()</span><span style=\"--0:#5D5F6E;--1:#B4A694\">;</span></div></div></code></pre><div class=\"copy\"><div aria-live=\"polite\"></div><button title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"(() => {  const TAG = &quot;#weekly&quot;;  const m = dv.current().file.name.match(/^(\\d{4})-W(\\d{2})$/);  if (!m) {    dv.paragraph(&quot;Weekly note filename must be like `2025-W50.md`.&quot;);    return;  }  function isoWeekStartISO(year, week) {    // Compute ISO week start (Monday) in UTC, then return YYYY-MM-DD    const simple = new Date(Date.UTC(year, 0, 1 + (week - 1) * 7));    const dow = simple.getUTCDay(); // 0..6, Sun..Sat    const start = new Date(simple);    if (dow <= 4) start.setUTCDate(simple.getUTCDate() - dow + 1);    else start.setUTCDate(simple.getUTCDate() + (8 - dow));    // YYYY-MM-DD    return start.toISOString().slice(0, 10);  }  const year = Number(m[1]);  const week = Number(m[2]);  const start = dv.date(isoWeekStartISO(year, week));  const end = start.plus({ days: 7 });  const pages = dv    .pages('&quot;Daily&quot;')    .where((p) => p.file.day &amp;&amp; p.file.day >= start &amp;&amp; p.file.day < end)    .sort((p) => p.file.day, &quot;asc&quot;);  let any = false;  for (const p of pages) {    const bullets = (p.file.lists ?? []).filter((li) =>      (li.text ?? &quot;&quot;).includes(TAG),    );    if (!bullets.length) continue;    any = true;    // [[YYYY-MM-DD]]    dv.paragraph(p.file.link.toString());    // - Line 1    // - Line 2    dv.list(      bullets        .map((li) => li.text ?? &quot;&quot;)        .map((t) => t.replace(TAG, &quot;&quot;).replace(/\\s+/g, &quot; &quot;).trim())        .filter(Boolean),    );    dv.paragraph(&quot;&quot;);  }  if (!any) dv.paragraph(`No ${TAG} bullets found for ${m[1]}-W${m[2]}.`);})();\"><div></div></button></div></figure></div>\n<p>The code is relatively simple: It finds the week number from the current file name, gathers the range of dates for that week, inspects each file for lines that contain <code>#weekly</code>, and then neatly lays them out grouped by date.</p>"
    },
    {
      "id": "https://msfjarvis.dev/notes/til-bypassing-aliases-in-a-bash-script/",
      "url": "https://msfjarvis.dev/notes/til-bypassing-aliases-in-a-bash-script/",
      "title": "TIL: Bypassing aliases in a Bash script",
      "date_published": "2025-12-07T08:11:00.000Z",
      "summary": "Quick syntactic solution for ensuring aliases don't mess up scripts",
      "content_html": "<p>On all my machines I have <code>cat</code> aliased to <a href=\"https://github.com/sharkdp/bat\">bat</a> which prints text with nice formatting and layout. This can sometimes trip up scripts if <code>bat</code> doesn’t correctly detect it is being used in a pipe and still outputs all the nice formatting instead of the plain text. To bypass this in your scripts, you can replace uses of <code>cat file_name</code> with <code>\\cat file_name</code> to always get the <code>cat</code> program in your <code>$PATH</code> instead of an alias. This also works with <code>\"cat\" file_name</code>.</p>"
    },
    {
      "id": "https://msfjarvis.dev/notes/migrating-a-postgres-database-between-servers/",
      "url": "https://msfjarvis.dev/notes/migrating-a-postgres-database-between-servers/",
      "title": "Migrating a Postgres database between servers",
      "date_published": "2025-11-08T07:48:00.000Z",
      "summary": "A quick rundown of the process so future me can have an easier time",
      "content_html": "<p>I’m having transitive network issues on one of my servers so I had to move out one of my services from the machine to a different one, which also involved transferring a Postgres database. The steps I followed for this are outlined below. I did this on two NixOS machines so your authentication setup and requirements may differ, please consult the documentation for the relevant commands to figure out what you need to do differently.</p>\n<h2 id=\"on-the-server-being-migrated-out-of\">On the server being migrated out of</h2>\n<div class=\"expressive-code\"><link rel=\"stylesheet\" href=\"https://msfjarvis.dev/_astro/ec.cteup.css\"><figure class=\"frame\"><figcaption class=\"header\"></figcaption><pre data-language=\"plain\"><code><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\"># Switch to the Postgres user</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">sudo -i su - postgres</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\"># Export just the roles so the imported database can have the right ownership</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\"># I also edited this to only keep the role for the db I was moving</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">pg_dumpall --roles-only &gt; roles.sql</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\"># Export the actual database in Postgres' binary format so its smaller</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">pg_dump -d atticd -Fc --create &gt; atticd.dump</span></div></div></code></pre><div class=\"copy\"><div aria-live=\"polite\"></div><button title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"# Switch to the Postgres usersudo -i su - postgres# Export just the roles so the imported database can have the right ownership# I also edited this to only keep the role for the db I was movingpg_dumpall --roles-only > roles.sql# Export the actual database in Postgres' binary format so its smallerpg_dump -d atticd -Fc --create > atticd.dump\"><div></div></button></div></figure></div>\n<p>Once I had both files I used <code>rsync</code> to ship them over to the new server</p>\n<h2 id=\"on-the-server-being-migrated-to\">On the server being migrated to</h2>\n<div class=\"expressive-code\"><figure class=\"frame\"><figcaption class=\"header\"></figcaption><pre data-language=\"plain\"><code><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">sudo -i su - postgres</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\"># Set up roles first</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">psql &lt; roles.sql</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\"># The `-d postgres` is required because pg_restore</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\"># wants to connect to at least one existing database before</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\"># doing anything, so we give it the one that exists by default</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\"># at least on NixOS.</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">pg_restore -d postgres ./atticd.dump</span></div></div></code></pre><div class=\"copy\"><div aria-live=\"polite\"></div><button title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"sudo -i su - postgres# Set up roles firstpsql < roles.sql# The `-d postgres` is required because pg_restore# wants to connect to at least one existing database before# doing anything, so we give it the one that exists by default# at least on NixOS.pg_restore -d postgres ./atticd.dump\"><div></div></button></div></figure></div>\n<p>And that’s it! After this I just enabled the service on the new server and it picked up the existing database right away.</p>"
    },
    {
      "id": "https://msfjarvis.dev/notes/gpt-5-seems-to-forget-things-well-within-its-context-window/",
      "url": "https://msfjarvis.dev/notes/gpt-5-seems-to-forget-things-well-within-its-context-window/",
      "title": "GPT-5 seems to forget things well within its context window",
      "date_published": "2025-11-03T11:02:00.000Z",
      "summary": "An instance of LLMs seemingly not being able to utilize their context window effectively",
      "content_html": "<blockquote>\n<p>I wish I had the session transcript to share for this but OpenCode doesn’t show my old session anymore so I can’t share it.</p>\n</blockquote>\n<p>Yesterday I threw GPT-5 via GitHub Copilot at a relatively simple problem: My Prometheus alerts give me a relatively useless message when an alert fires from the <a href=\"https://github.com/prometheus/blackbox_exporter\">prometheus-blackbox-exporter</a>. I would like to see the URL that it’s failing to reach. I used <a href=\"https://opencode.ai/\">OpenCode</a> to drive this in an agentic manner.</p>\n<p>At one point, the LLM hallucinated a <code>subject</code> field in the Prometheus <a href=\"https://prometheus.io/docs/alerting/latest/configuration/#email_config\">Alertmanager config for email alerts</a>. I pointed out the error, after which it changed the <code>subject</code> to correctly be <code>headers { Subject = \"...\"; };</code> instead. A few iterations on other parts of the config later, it randomly went back to that line and reverted it back to the wrong <code>subject</code> field saying that the right version was actually incorrect.</p>\n<p>This happened around the 45K tokens mark in its context window, which is well under the claimed values. Ultimately this was an easy mistake to fix again, but I was relatively surprised to see the LLM repeat a mistake within the same session.</p>"
    },
    {
      "id": "https://msfjarvis.dev/notes/copying-nix-flake-inputs-to-a-different-machine/",
      "url": "https://msfjarvis.dev/notes/copying-nix-flake-inputs-to-a-different-machine/",
      "title": "Copying Nix Flake inputs to a different machine",
      "date_published": "2025-11-02T09:48:00.000Z",
      "summary": "Simple one-liner to avoid re-downloading inputs on a different machine on your LAN",
      "content_html": "<blockquote>\n<p>TL;DR: <code>nix flake archive --json | jq -r '.inputs[].path' | xargs nix --verbose copy --to ssh://user@host</code></p>\n</blockquote>\n<p>Due to unfortunate circumstances my homelab server is running on a relatively slow internet connection while I’m at home. To alleviate the problems this causes with NixOS updates, I build and deploy the new generations for it from my desktop. This works in most cases, except when an update ends up restarting Tailscale or OpenSSH which cuts off my SSH session and leaves the machine in a bad state.</p>\n<p>As a workaround for <em>that</em> problem I now copy the built closure to the Nix Store of the machine and do an <code>nh os switch</code> inside a tmux session there so it can run to completion without having to download everything. This usually works, but it does need to redownload inputs which seems a little wasteful so I searched around for ways to get the Nix Store paths of all my Flake inputs and came up with this small solution that lets me skip even more network I/O for the homelab.</p>"
    },
    {
      "id": "https://msfjarvis.dev/notes/changing-locale-set-maching-running-postgres/",
      "url": "https://msfjarvis.dev/notes/changing-locale-set-maching-running-postgres/",
      "title": "Changing the locale set on a maching running Postgres",
      "date_published": "2025-10-29T18:30:11.000Z",
      "content_html": "<p>I keep rediscovering that Postgres uses the system locale and timezone database, despite <a href=\"https://github.com/miniflux/v2/issues/3673\">having read other people’s woes</a>.</p>\n<p>I changed the locale in my NixOS configuration from en_US to en_GB to appease the spell checker on my desktop. When I deployed this updated configuration to one of my servers, all my services immediately fell over.</p>\n<p>I had the sense to look at <code>journalctl</code> right away which made it very apparent what the problem was:</p>\n<div class=\"expressive-code\"><link rel=\"stylesheet\" href=\"https://msfjarvis.dev/_astro/ec.cteup.css\"><figure class=\"frame\"><figcaption class=\"header\"></figcaption><pre data-language=\"plaintext\"><code><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">Oct 29 18:27:00 wailord postgres[3593]: [3593] FATAL:  database locale is incompatible with operating system</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">Oct 29 18:27:00 wailord postgres[3593]: [3593] DETAIL:  The database was initialized with LC_COLLATE \"en_US.UTF-8\",  which is not recognized by setlocale().</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">Oct 29 18:27:00 wailord postgres[3593]: [3593] HINT:  Recreate the database with another locale or install the missing locale.</span></div></div></code></pre><div class=\"copy\"><div aria-live=\"polite\"></div><button title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"Oct 29 18:27:00 wailord postgres[3593]: [3593] FATAL:  database locale is incompatible with operating systemOct 29 18:27:00 wailord postgres[3593]: [3593] DETAIL:  The database was initialized with LC_COLLATE &quot;en_US.UTF-8&quot;,  which is not recognized by setlocale().Oct 29 18:27:00 wailord postgres[3593]: [3593] HINT:  Recreate the database with another locale or install the missing locale.\"><div></div></button></div></figure></div>\n<p>(Yes my server is called <a href=\"https://pokemondb.net/pokedex/wailord\">wailord</a>, I was loving <a href=\"https://cobblemon.com/en\">Cobblemon</a> when it was provisioned)</p>\n<p>The solution was to simply add <code>en_US</code> back to <a href=\"https://github.com/NixOS/nixpkgs/blob/fc2ba10c4f0e1d4cda694e730769c563c2145745/nixos/modules/config/i18n.nix#L117\"><code>i18n.supportedLocales</code></a> and a rebuild fixed everything right up.</p>"
    },
    {
      "id": "https://msfjarvis.dev/notes/welcome-to-notes/",
      "url": "https://msfjarvis.dev/notes/welcome-to-notes/",
      "title": "Welcome to Notes",
      "date_published": "2025-10-29T13:48:00.000Z",
      "content_html": "<p>New section woo! This is mostly going to be used for relatively smaller things that I don’t find worthy of a blogpost but are a bit awkward to fit into a <a href=\"https://fantastic.earth/@msfjarvis\">toot</a>.</p>\n<p>Over time I plan to backfill this with the contents of my Obsidian Vault’s “TIL” folder and then just use this public listing for the purpose, since it’s equally accessible to me via the excellent <a href=\"https://github.com/sveltia/sveltia-cms/\">Sveltia CMS</a>.</p>"
    },
    {
      "id": "https://msfjarvis.dev/notes/adding-boarding-passes-google-wallet/",
      "url": "https://msfjarvis.dev/notes/adding-boarding-passes-google-wallet/",
      "title": "Adding boarding passes to Google Wallet",
      "date_published": "2025-10-17T22:34:40.000Z",
      "content_html": "<p>Google Wallet lets you add passes via image, so rather than try to share a PDF with it you can take a screenshot and share that to the app instead. “Google AI” does the rest.</p>"
    },
    {
      "id": "https://msfjarvis.dev/notes/interactive-reverts-git/",
      "url": "https://msfjarvis.dev/notes/interactive-reverts-git/",
      "title": "Interactive reverts in Git",
      "date_published": "2025-09-23T21:53:23.000Z",
      "content_html": "<p>Git has the capability to revert only specific hunks of a commit using the <code>git checkout --patch</code> command. To revert the changes to file <code>example.md</code> made in commit <code>0xDEADBEEF</code>, you would run <code>git checkout --patch 0xDEADBEEF^ example.md</code> and Git will open up the interactive diff editor so you can select which reverts you want to apply to your index.</p>"
    },
    {
      "id": "https://msfjarvis.dev/notes/debugging-issues-provisioning-certificates-from-let/",
      "url": "https://msfjarvis.dev/notes/debugging-issues-provisioning-certificates-from-let/",
      "title": "Debugging issues with provisioning certificates from Let's Encrypt",
      "date_published": "2025-09-16T01:30:40.000Z",
      "content_html": "<p><a href=\"https://letsdebug.net\">Let’s Debug</a> provides a way to test if your domain is set up to correctly answer Let’s Encrypt challenges.</p>"
    },
    {
      "id": "https://msfjarvis.dev/notes/oracle-cloud-network-policy/",
      "url": "https://msfjarvis.dev/notes/oracle-cloud-network-policy/",
      "title": "Oracle Cloud's restrictive default network policy",
      "date_published": "2025-09-16T01:30:40.000Z",
      "content_html": "<p>Oracle cloud doesn’t enable any ports other than SSH by default, which somehow doesn’t affect any Tailscale services but breaks the ability to negotiate a TLS certificate over ports 80 and 443.</p>"
    },
    {
      "id": "https://msfjarvis.dev/notes/cargo-failing-gitignore-d-files-while-publishing/",
      "url": "https://msfjarvis.dev/notes/cargo-failing-gitignore-d-files-while-publishing/",
      "title": "Cargo failing at gitignore-d files while publishing",
      "date_published": "2025-09-14T22:31:48.000Z",
      "content_html": "<p>Finally managed to find a workaround for the Rust problem I was having with <code>cargo publish</code> picking up files from my gitignore-d <code>.direnv</code> directory and complaining about them being uncommitted.</p>\n<p>This was the result of cascading surprises: setting <code>package.includes</code> causes gitignore to no longer be considered, and <code>\"README.md\"</code> in <code>package.includes</code> actually works like <code>\"**/README.md\"</code>’ and picks up the READMEs from the repositories in <code>.direnv/flake-inputs/</code>.</p>\n<p>Upstream issue: <a href=\"https://github.com/rust-lang/cargo/issues/12294#issuecomment-2171044946\">https://github.com/rust-lang/cargo/issues/12294#issuecomment-2171044946</a></p>"
    },
    {
      "id": "https://msfjarvis.dev/notes/grafana-config-helper-read-values-runtime/",
      "url": "https://msfjarvis.dev/notes/grafana-config-helper-read-values-runtime/",
      "title": "Grafana config helper to read values at runtime",
      "date_published": "2025-08-27T22:12:03.000Z",
      "content_html": "<p>Grafana has a helper in its config files called <code>$__file{/path/to/file}</code> that will read the file at runtime, making it easier to configure secrets via Nix.</p>"
    },
    {
      "id": "https://msfjarvis.dev/notes/sdkman-can-use-your-system-java-installations/",
      "url": "https://msfjarvis.dev/notes/sdkman-can-use-your-system-java-installations/",
      "title": "SDKMAN can use your system Java installations",
      "date_published": "2025-07-03T13:54:19.000Z",
      "content_html": "<p><a href=\"https://sdkman.io\">SDKMAN</a> can symlink existing Java installations into its own managed thing so we can have the best of both worlds by installing Java from Homebrew but managing the JAVA_HOME with SDKMAN.</p>"
    },
    {
      "id": "https://msfjarvis.dev/notes/testing-gitlab-ci-pipelines-locally/",
      "url": "https://msfjarvis.dev/notes/testing-gitlab-ci-pipelines-locally/",
      "title": "Testing GitLab CI pipelines locally",
      "date_published": "2025-07-03T13:54:19.000Z",
      "content_html": "<p>GitLab CI pipelines can be executed locally using <a href=\"https://github.com/firecow/gitlab-ci-local\">firecow/gitlab-ci-local</a></p>\n<p>This used to be a first party feature within the official gitlab-runner binary but they deleted it citing maintenance burden. What a shame.</p>"
    },
    {
      "id": "https://msfjarvis.dev/notes/building-derivations-nix-repl/",
      "url": "https://msfjarvis.dev/notes/building-derivations-nix-repl/",
      "title": "Building derivations in the Nix REPL",
      "date_published": "2025-06-30T20:01:25.000Z",
      "content_html": "<p>In a Nix REPL you can run <code>:bl inputs.nixpkgs.legacyPackages.aarch64-linux.pkgs.attic-server</code> to build the derivation and create a <code>result</code> link. Can also run <code>:bl nixosConfigurations.ryzenbox.pkgs.attic-client</code>.</p>"
    },
    {
      "id": "https://msfjarvis.dev/notes/combining-videos-using-ffmpeg/",
      "url": "https://msfjarvis.dev/notes/combining-videos-using-ffmpeg/",
      "title": "Combining videos using FFmpeg",
      "date_published": "2025-06-30T20:01:25.000Z",
      "content_html": "<p>FFmpeg lets you mix videos together from different sources based on a zero-indexed map of input files. For the diagram given below, you can do <code>ffmpeg -i input_0.mp4 -i input_1.mp4 -c copy -map 0:v:0 -map 1:a:0 -shortest output.mp4</code> to take the video from <code>input_0.mp4</code> and audio from <code>input_1.mp4</code>.\n<a href=\"https://i.sstatic.net/3ep4f.png\" class=\"pswp-gallery-item\" aria-label=\"\"><img src=\"https://i.sstatic.net/3ep4f.png\" alt=\"\"></a></p>"
    },
    {
      "id": "https://msfjarvis.dev/notes/concatenating-videos-ffmpeg/",
      "url": "https://msfjarvis.dev/notes/concatenating-videos-ffmpeg/",
      "title": "Concatenating videos with FFmpeg",
      "date_published": "2025-06-30T20:01:25.000Z",
      "content_html": "<p>FFmpeg has support for concatenating files with no re-encoding</p>\n<div class=\"expressive-code\"><link rel=\"stylesheet\" href=\"https://msfjarvis.dev/_astro/ec.cteup.css\"><figure class=\"frame has-title\"><figcaption class=\"header\"><span class=\"title\">file_list.txt</span></figcaption><pre data-language=\"plaintext\"><code><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">file 'source1.mp4'</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">file 'source2.mp4'</span></div></div><div class=\"ec-line\"><div class=\"code\">\n</div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">$ ffmpeg -f concat -safe 0 -i file_list.txt -c copy output.mp4</span></div></div></code></pre><div class=\"copy\"><div aria-live=\"polite\"></div><button title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"file 'source1.mp4'file 'source2.mp4'$ ffmpeg -f concat -safe 0 -i file_list.txt -c copy output.mp4\"><div></div></button></div></figure></div>"
    },
    {
      "id": "https://msfjarvis.dev/notes/configuring-integrations-miniflux/",
      "url": "https://msfjarvis.dev/notes/configuring-integrations-miniflux/",
      "title": "Configuring integrations in Miniflux",
      "date_published": "2025-06-30T20:01:25.000Z",
      "content_html": "<p>Miniflux requires setting up individual credentials for each integration, and the password can not be the same as your normal account password.</p>"
    },
    {
      "id": "https://msfjarvis.dev/notes/configuring-windows-dual-boot-nixos-automatically/",
      "url": "https://msfjarvis.dev/notes/configuring-windows-dual-boot-nixos-automatically/",
      "title": "Configuring Windows dual-boot on NixOS automatically",
      "date_published": "2025-06-30T20:01:25.000Z",
      "content_html": "<p>NixOS has a module option in <code>systemd-boot</code> that automatically configures boot entries for Windows without requiring me to copy over the EFI folder [<a href=\"https://search.n%C3%83%C2%BCschtos.de/?scope=NixOS&amp;option_scope=11&amp;query=boot.loader.systemd-boot.windows&amp;option=boot.loader.systemd-boot.windows\">ref</a>]</p>"
    },
    {
      "id": "https://msfjarvis.dev/notes/copying-timestamp-file/",
      "url": "https://msfjarvis.dev/notes/copying-timestamp-file/",
      "title": "Copying the timestamp of a file",
      "date_published": "2025-06-30T20:01:25.000Z",
      "content_html": "<p>You can use <code>touch -r old new</code> to copy the timestamp of an older file in a newly created file. Useful for doing bulk re-encodes with FFmpeg that benefit from preserving timestamps.</p>"
    },
    {
      "id": "https://msfjarvis.dev/notes/default-padding-compose-material3-scaffold/",
      "url": "https://msfjarvis.dev/notes/default-padding-compose-material3-scaffold/",
      "title": "Default padding in Compose Material3 Scaffold",
      "date_published": "2025-06-30T20:01:25.000Z",
      "content_html": "<p>The default Material3 <code>Scaffold</code> component adds the height of the <code>bottomBar</code> parameter to the inner padding which resulted in the issues I was having with the floating navigation bar. Removing all calls to <code>Modifier.padding</code> and wiring down the padding parameter as <code>contentPadding</code> resolved the problem.</p>"
    },
    {
      "id": "https://msfjarvis.dev/notes/disk-management-woes/",
      "url": "https://msfjarvis.dev/notes/disk-management-woes/",
      "title": "Disk management woes",
      "date_published": "2025-06-30T20:01:25.000Z",
      "content_html": "<p>GNOME does not have support to move partitions around.</p>\n<p>GParted requires the presence of <code>ntfs3g</code> on <code>$PATH</code> to enable NTFS support.</p>\n<p>When moving Windows installations across disks the reason why stuff doesn’t boot is that the Windows EFI for the old disk is sitting around in your <code>/boot</code> partition so the <code>systemd-boot</code> option I pick tries to boot into a non-existent installation. The fix for this is to go and delete the <code>Microsoft</code> folder from <code>/boot/EFI</code> and replace it with the one in your new disk and it’ll be fixed.</p>\n<p>To extend an NTFS partition in Windows just use the good old <code>diskmgmt.msc</code> and it can just extend it while the disk is online!</p>\n<p>To move the Windows recovery partition around, run <code>reagentc /disable</code> to disable the recovery agent, then go into GParted to move the partition to the end and then boot back into #Windows. Do the necessary partition changes then run <code>reagentc /enable</code> to enable it.</p>"
    },
    {
      "id": "https://msfjarvis.dev/notes/documentation-error-codes-used-apple/",
      "url": "https://msfjarvis.dev/notes/documentation-error-codes-used-apple/",
      "title": "Documentation for error codes used in Apple's APIs",
      "date_published": "2025-06-30T20:01:25.000Z",
      "content_html": "<p>Apple error codes are indexed at <a href=\"https://osstatus.com\">https://osstatus.com</a>, searchable by hex, long, and string representation. Invaluable for debugging errors thrown by WebRTC.</p>"
    },
    {
      "id": "https://msfjarvis.dev/notes/finding-error-logs-from-plausible/",
      "url": "https://msfjarvis.dev/notes/finding-error-logs-from-plausible/",
      "title": "Finding error logs from Plausible",
      "date_published": "2025-06-30T20:01:25.000Z",
      "content_html": "<p>Plausible writes error logs only into its Postgres database. Errors are in the <code>errors</code> column of the <code>oban_jobs</code> table.</p>"
    },
    {
      "id": "https://msfjarvis.dev/notes/fun-pelican-helldivers-2/",
      "url": "https://msfjarvis.dev/notes/fun-pelican-helldivers-2/",
      "title": "Fun with the Pelican in Helldivers 2",
      "date_published": "2025-06-30T20:01:25.000Z",
      "content_html": "<p>In Helldivers 2 you can cause the Pelican to launch itself in the air upside down by calling in a support weapon right under the wing. On impact the Pelican will go flying in the air and land in a random spot.</p>"
    },
    {
      "id": "https://msfjarvis.dev/notes/getting-full-systemd-unit-file-service/",
      "url": "https://msfjarvis.dev/notes/getting-full-systemd-unit-file-service/",
      "title": "Getting the full systemd unit file for a service",
      "date_published": "2025-06-30T20:01:25.000Z",
      "content_html": "<p>You can run <code>systemctl show servicename.service</code> to get the “final” version of your systemd service file, including all overrides which would typically be loaded dynamically by systemd itself and not visible in the actual unit file.</p>\n<p>This is extremely helpful on NixOS where packages can sometimes re-use upstream systemd unit files and inject NixOS specific options through an override file.</p>"
    },
    {
      "id": "https://msfjarvis.dev/notes/git-credential-helpers/",
      "url": "https://msfjarvis.dev/notes/git-credential-helpers/",
      "title": "Git credential helpers",
      "date_published": "2025-06-30T20:01:25.000Z",
      "content_html": "<p>Git credential helpers work via writing and reading from standard input and standard output which helped me debug an issue I was having with <a href=\"https://github.com/languitar/pass-git-helper\">pass-git-helper</a>. To get a credential helper to spit out the password, invoke it with the <code>get</code> action and pass in some basic <code>key=value</code> data. The interaction looks somewhat like given below, with <code>&lt;</code> indicating the standard input of the credential helper and <code>&gt;</code> indicating its standard output.</p>\n<div class=\"expressive-code\"><link rel=\"stylesheet\" href=\"https://msfjarvis.dev/_astro/ec.cteup.css\"><figure class=\"frame\"><figcaption class=\"header\"></figcaption><pre data-language=\"plaintext\"><code><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\"># for URL https://git.msfjarvis.dev/msfjarvis/pass-store</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">$ pass-git-helper get</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">&lt; protocol=https</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">&lt; host=git.msfjarvis.dev</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">&lt; path=msfjarvis/pass-store</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">&gt; protocol=https</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">&gt; host=git.msfjarvis.dev</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">&gt; username=msfjarvis</span></div></div><div class=\"ec-line\"><div class=\"code\"><span style=\"--0:#4c4f69;--1:#ebdbb2\">&gt; password=hunter2</span></div></div></code></pre><div class=\"copy\"><div aria-live=\"polite\"></div><button title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"# for URL https://git.msfjarvis.dev/msfjarvis/pass-store$ pass-git-helper get< protocol=https< host=git.msfjarvis.dev< path=msfjarvis/pass-store> protocol=https> host=git.msfjarvis.dev> username=msfjarvis> password=hunter2\"><div></div></button></div></figure></div>"
    },
    {
      "id": "https://msfjarvis.dev/notes/gnome-keyring-daemon-failing/",
      "url": "https://msfjarvis.dev/notes/gnome-keyring-daemon-failing/",
      "title": "GNOME keyring daemon failing with 'No such secret item at path'",
      "date_published": "2025-06-30T20:01:25.000Z",
      "content_html": "<p>After a GNOME related NixOS rebuild sometimes the secret store starts erroring out with this message: <code>No such secret item at path: /org/freedesktop/secrets/collection/login/145</code></p>\n<p>This is resolved by restarting the gnome-keyring-daemon using the command <code>gnome-keyring-daemon -r -d</code>.</p>"
    },
    {
      "id": "https://msfjarvis.dev/notes/hades-hades-2-are-written-lua/",
      "url": "https://msfjarvis.dev/notes/hades-hades-2-are-written-lua/",
      "title": "Hades and Hades 2 are written in Lua",
      "date_published": "2025-06-30T20:01:25.000Z",
      "content_html": "<p>Hades and Hades_2 are written mostly in Lua and all the code is left as-is in the game which makes modding significantly simpler. Very cool!</p>"
    },
    {
      "id": "https://msfjarvis.dev/notes/handle_path-caddy/",
      "url": "https://msfjarvis.dev/notes/handle_path-caddy/",
      "title": "`handle_path` in Caddy",
      "date_published": "2025-06-30T20:01:25.000Z",
      "content_html": "<p>Caddy has a <code>handle_path</code> directive that you can use to host services that don’t like being hosted on paths, it will strip the prefix for the underlying service so it sees just the root domain</p>"
    }
  ]
}