This is a valid RSS feed.
This feed is valid, but interoperability with the widest range of feed readers could be improved by implementing the following recommendations.
line 38, column 0: (7 occurrences) [help]
<picture><source
line 45, column 0: (8 occurrences) [help]
<img decoding="async" src="https://akrabat.com/wp-content/uploads/2025/0 ...
line 45, column 0: (7 occurrences) [help]
<img decoding="async" src="https://akrabat.com/wp-content/uploads/2025/0 ...
line 298, column 0: (2 occurrences) [help]
Rob Allen <rob@akrabat.com>
line 366, column 3: (2 occurrences) [help]
]]></content:encoded>
^
<p><img fetchpriority="high" decoding="async" src="https://akrabat.com/wp-co ...
<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:wfw="http://wellformedweb.org/CommentAPI/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
>
<channel>
<title>Rob Allen</title>
<atom:link href="https://akrabat.com/feed/" rel="self" type="application/rss+xml" />
<link>https://akrabat.com</link>
<description>Pragmatism in today's world</description>
<lastBuildDate>Sat, 06 Sep 2025 16:28:54 +0000</lastBuildDate>
<language>en-US</language>
<sy:updatePeriod>
hourly </sy:updatePeriod>
<sy:updateFrequency>
1 </sy:updateFrequency>
<generator>https://wordpress.org/?v=6.8.2</generator>
<item>
<title>Opening Slack Jira Cloud links in the right browser</title>
<link>https://akrabat.com/opening-slack-jira-cloud-app-links-in-the-right-browser/</link>
<comments>https://akrabat.com/opening-slack-jira-cloud-app-links-in-the-right-browser/#respond</comments>
<dc:creator><![CDATA[Rob]]></dc:creator>
<pubDate>Tue, 30 Sep 2025 10:00:00 +0000</pubDate>
<category><![CDATA[Computing]]></category>
<guid isPermaLink="false">https://akrabat.com/?p=7499</guid>
<description><![CDATA[I use OpenIn to open links in a given browser when I click on them in other applications. This is really helpful to keep various work related stuff in different browsers or profiles and I find it very helpful. One thing that's bothered me is that links from the Jira Cloud Slack App ignore my OpenIn rules and always open in Safari and I finally sat down to work out why. The investigation I create… <a href="https://akrabat.com/opening-slack-jira-cloud-app-links-in-the-right-browser/">continue reading</a>.]]></description>
<content:encoded><![CDATA[<p>I use <a href="https://loshadki.app/openin4/">OpenIn</a> to open links in a given browser when I click on them in other applications. This is really helpful to keep various work related stuff in different browsers or profiles and I find it very helpful.</p>
<p>One thing that's bothered me is that links from the <a href="https://slack.com/marketplace/A2RPP3NFR-jira-cloud">Jira Cloud Slack App</a> ignore my OpenIn rules and always open in Safari and I finally sat down to work out why.</p>
<h2>The investigation</h2>
<p>I create a new OpenIn rule and enabled multiple browsers so that OpenIn would present a choice window to me.</p>
<p>It looks like this</p>
<picture><source
srcset="https://akrabat.com/wp-content/uploads/2025/09/2025slack-app-linkopen-in-choices-dark.png"
media="(prefers-color-scheme: dark)"
/><source
srcset="https://akrabat.com/wp-content/uploads/2025/09/2025slack-app-linkopen-in-choices-light.png"
media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)"
/><br />
<img decoding="async" src="https://akrabat.com/wp-content/uploads/2025/09/2025slack-app-linkopen-in-choices-light.png" loading="lazy" alt="Slack app linkopen in choices light." class="no-border" width="266"/>
</picture>
<p>At the bottom, we can see the link that OpenIn has received.</p>
<p>Even though the presented URL in the Slack app is <tt>https://my-client.atlassian.net/browser/PROJ-123</tt>, and if you right click and copy link, that's what you get in your clipboard, when you <em>click</em> on the link, you get a slack.com link.</p>
<p>Copying that link, it's of the form: <tt>https://slack.com/openid/connect/login_initiate_redirect?login_hint=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.blah.blah</tt>. </p>
<p>That's interesting! I've seen that <tt>eyJhbGci</tt> prefix often enough to know on sight that it's the start of base64 encoded <a href="https://www.rfc-editor.org/rfc/rfc7519">JWT</a> and sure enough, base64 decoding it proved that it was.</p>
<p>The format of JWT is <tt>{header}.{payload}.{signature}</tt> and it's unencrypted, so we can inspect the payload easily enough. This is redacted, but it's of the form:</p>
<pre>
{
"aud" : "123456789.123456789",
"auth_time" : 1758615553,
"exp" : 1758619153,
"https://slack.com/target_uri" : "https://{my-client}.atlassian.net/browse/PROJ-123",
"https://slack.com/team_id" : "ABCDEFGHI",
"https://slack.com/user_id" : "U01AB2CDEF3",
"iat" : 1758615553,
"iss" : "https://slack.com",
"sub" : "rob@{my-client}.com"
}
</pre>
<p>We can see that the URL that we want to go to is in the payload's <tt>"https://slack.com/target_uri"</tt> property, so we <em>just</em> need to set up OpenIn to use that URL and pick the appropriate browser.</p>
<h2>OpenIn's custom scripts</h2>
<p>One really nice feature of OpenIn is that you can write <a href="https://loshadki.app/openin4/080-javascript-for-rule/">custom scripts for a rule</a>, so we can use this to extract the payload and pick the browser.</p>
<p>So I read the web page and wrote some Javascript!</p>
<p>The work we need to do is:</p>
<ol>
<li>Extract the payload from the query parameter</li>
<li>JSON decode it</li>
<li>Read the target_uri</li>
<li>Choose browser based on target_uri</li>
</ol>
<p>This needs a few helper functions</p>
<h3>Helper functions</h3>
<p>Extracting the payload is straightforward JS. We need the text between the two <tt>.</tt>s:</p>
<pre lang="javascript">
function extractJwtPayload(str) {
const parts = str.split('.');
return parts.length >= 3 ? parts[1] : '';
}
</pre>
<p>As OpenIn uses WebKit's <a href="https://docs.webkit.org/Deep%20Dive/JSC/JavaScriptCore.html">JavaScriptCore</a>, we can decode base64 using <tt>Uint8Array.fromBase64()</tt>:</p>
<pre lang="javascript">
function base64Decode(str) {
const uint8Array = Uint8Array.fromBase64(str);
return String.fromCharCode(...uint8Array);
}
</pre>
<p>We also need to select the browser that we want the link to open in. This is done in OpenIn by setting all the "visible" browsers to false, except the one you want:</p>
<pre lang="javascript">
function selectBrowser(browser) {
let apps = ctx.getApps()
apps.forEach(function (app) {
app.visible = (app.name == browser)
})
}
</pre>
<h3>Do the work</h3>
<p>Having set everything up, we can now find the <tt>target_uri</tt> and choose the browser. </p>
<p>Firstly we only want to do this work if the source app is Slack and that we have a <tt>login_hint</tt> parameter:</p>
<pre lang="javascript">
if (ctx.getSourceApp().path.startsWith("/Applications/Slack.app")
&& ctx.url.searchParams.has('login_hint')) {
</pre>
<p>Extracting the <tt>target_uri</tt> is a case of using the functions we've written:</p>
<pre lang="javascript">
const login_hint = ctx.url.searchParams.get('login_hint');
const jwtPayload = extractJwtPayload(login_hint);
const payload = base64Decode(jwtPayload).replace(/\0/g, '');
const data = JSON.parse(payload);
const target_uri = data['https://slack.com/target_uri'];
</pre>
<p>Note that I discovered some null bytes during testing, so removed them from the decoded string.</p>
<p>Now we set OpenIn's URI and select the browser we want:</p>
<pre>
// Set OpenIn's URL
ctx.url.href=target_uri;
// Select the browser
if (target_uri.includes("my-client-1")) {
selectBrowser("Firefox");
return;
}
if (target_uri.includes("my-client-2")) {
selectBrowser("Firefox");
return;
}
// Default to Safari
selectBrowser("Safari");
return;
</pre>
<p>and we're done.</p>
<h2>That's it</h2>
<p>That's it! Whenever I click on a Jira link in Slack, the correct browser opens directly to where I want to go.</p>
<p>The full script is here: <a href="https://akrabat.com/wp-content/uploads/2025-09-openin-slack-app-link.js">openin-slack-app-link.js</a></p>
]]></content:encoded>
<wfw:commentRss>https://akrabat.com/opening-slack-jira-cloud-app-links-in-the-right-browser/feed/</wfw:commentRss>
<slash:comments>0</slash:comments>
</item>
<item>
<title>Fixing PostgreSQL collation version mismatch</title>
<link>https://akrabat.com/fixing-postgresql-collation-version-mismatch/</link>
<comments>https://akrabat.com/fixing-postgresql-collation-version-mismatch/#respond</comments>
<dc:creator><![CDATA[Rob]]></dc:creator>
<pubDate>Tue, 23 Sep 2025 10:00:00 +0000</pubDate>
<category><![CDATA[PostgreSQL]]></category>
<guid isPermaLink="false">https://akrabat.com/?p=7493</guid>
<description><![CDATA[After pulling a new version of the Docker PostgreSQL container, I started getting this warning: WARNING: database "dev" has a collation version mismatch DETAIL: The database was created using collation version 2.36, but the operating system provides version 2.41. HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE dev REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. This seems to have occurred because the underlying… <a href="https://akrabat.com/fixing-postgresql-collation-version-mismatch/">continue reading</a>.]]></description>
<content:encoded><![CDATA[<p>After pulling a new version of the Docker PostgreSQL container, I started getting this warning:</p>
<pre>
WARNING: database "dev" has a collation version mismatch
DETAIL: The database was created using collation version 2.36, but the operating system provides version 2.41.
HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE dev REFRESH COLLATION VERSION, or build PostgreSQL with the right library version.
</pre>
<p>This seems to have occurred because the underlying OS was changed to Trixie from Bullseye for the default image. i.e. I used:</p>
<pre>
image: postgres:17
</pre>
<p>Rather than:</p>
<pre>
image: postgres:17-bookworm
</pre>
<p>This is on my dev machine, so it's not a major problem as usually I can just blow away the database and recreate it. However, I have some test data that I don't want to lose quite yet, so I took a backup using <tt>pg_dump</tt> and then did this instead:</p>
<pre>
REINDEX DATABASE dev;
ALTER DATABASE dev REFRESH COLLATION VERSION;
</pre>
<p>The warning's now gone. </p>
<p>Of course, in production, you'll want to be a bit more careful and a <tt>pg_dump</tt> and load is more likely to avoid any risks of corruption. As ever, YMMV.</p>
]]></content:encoded>
<wfw:commentRss>https://akrabat.com/fixing-postgresql-collation-version-mismatch/feed/</wfw:commentRss>
<slash:comments>0</slash:comments>
</item>
<item>
<title>Jumping to the end of bash's history</title>
<link>https://akrabat.com/jumping-to-the-end-of-bashs-history/</link>
<comments>https://akrabat.com/jumping-to-the-end-of-bashs-history/#respond</comments>
<dc:creator><![CDATA[Rob]]></dc:creator>
<pubDate>Tue, 16 Sep 2025 10:00:00 +0000</pubDate>
<category><![CDATA[Command Line]]></category>
<category><![CDATA[Shell Scripting]]></category>
<guid isPermaLink="false">https://akrabat.com/?p=7490</guid>
<description><![CDATA[I use bash's history all the time, via ctrl+r and also with the up and down keys; it's wonderful. Sometimes, I want to get back to the end of my history and I recently discovered that there's a shortcut for this: meta+>. It doesn't matter where you are in your history, pressing meta+> jumps you to the end and you have a blank prompt again. I use iTerm2 on my Mac and have my right… <a href="https://akrabat.com/jumping-to-the-end-of-bashs-history/">continue reading</a>.]]></description>
<content:encoded><![CDATA[<p>I use bash's history all the time, via <tt>ctrl+r</tt> and also with the <a href="https://akrabat.com/context-specific-history-at-the-bash-prompt/"><tt>up and down keys</tt></a>; it's wonderful.</p>
<p>Sometimes, I want to get back to the end of my history and I recently discovered that there's a shortcut for this: <tt>meta+></tt>. It doesn't matter where you are in your history, pressing <tt>meta+></tt> jumps you to the end and you have a blank prompt again.</p>
<p>I use <a href="https://iterm2.com">iTerm2</a> on my Mac and have my right hand <tt>option</tt> key set to <tt>meta</tt>. This is done in Settings→Profiles→Keys, setting "Right Option (C) key:" to "Esc+".</p>
<p>However, to press <tt>meta+></tt>, I need to do <tt>right-option+shift+.</tt> which isn't as easy as <tt>right-option+.</tt>, so let's rebind!</p>
<p>To rebind, I looked up the bash command for this functionality (`end-of-history`), and then added this to my <tt>.bashrc</tt>:</p>
<pre>
bind '"\e.": end-of-history'
</pre>
<p>All done. Now I just press <tt>right-option+.</tt> and I'm back at the end of history as if I'd never navigated it.</p>
]]></content:encoded>
<wfw:commentRss>https://akrabat.com/jumping-to-the-end-of-bashs-history/feed/</wfw:commentRss>
<slash:comments>0</slash:comments>
</item>
<item>
<title>Converting JWKS JSON to PEM using Python</title>
<link>https://akrabat.com/converting-jwks-json-to-pem-using-python/</link>
<comments>https://akrabat.com/converting-jwks-json-to-pem-using-python/#respond</comments>
<dc:creator><![CDATA[Rob]]></dc:creator>
<pubDate>Tue, 09 Sep 2025 10:00:00 +0000</pubDate>
<category><![CDATA[Command Line]]></category>
<category><![CDATA[Python]]></category>
<guid isPermaLink="false">https://akrabat.com/?p=7484</guid>
<description><![CDATA[Following on from my earlier exploration of JWKS (RFC7517), I found myself needing to convert the JWKS into PEM format. This time I turned to Python with my preference of using uv with inline script metadata and created jwks-to-pem.py. The really nice thing about inline script metadata is that we can use the cryptography package to do all the hard work with RSA and serialisation. We just have to remember that the base64 encoded values… <a href="https://akrabat.com/converting-jwks-json-to-pem-using-python/">continue reading</a>.]]></description>
<content:encoded><![CDATA[<p>Following on from my <a href="https://akrabat.com/creating-jwks-json-file-in-php/">earlier exploration of JWKS</a> (<a href="https://www.rfc-editor.org/rfc/rfc7517">RFC7517</a>), I found myself needing to convert the JWKS into PEM format.</p>
<p>This time I turned to Python with my preference of using <a href="https://github.com/astral-sh/uv">uv</a> with <a href="https://akrabat.com/defining-python-dependencies-at-the-top-of-the-file/">inline script metadata</a> and created <a href="#script"><tt>jwks-to-pem.py</tt></a>.</p>
<p>The really nice thing about inline script metadata is that we can use the <a href="https://pypi.org/project/cryptography/">cryptography package</a> to do all the hard work with RSA and serialisation. We just have to remember that the base64 encoded values are <a href="https://datatracker.ietf.org/doc/html/rfc4648#section-5">base64 URL encoded</a> and account for it.</p>
<p>As a single file python script, I make it executable with <tt>chmod +x jwks-to-pem.py</tt> and made it so that I can pipe the output of a <a href="https://curl.se">curl</a> call to it, or pass in a JSON file. I prefer to use the <tt>curl</tt> solution though with:</p>
<pre>
curl -s https://example.com/.well-known/jwks.json | jwks-to-pem.py
</pre>
<h3>Example</h3>
<p>Here's an example from the <a href="https://developer.api.apps.cam.ac.uk/docs/oauth2/1/routes/.well-known/jwks.json/get">University of Cambridge</a>.</p>
<p>On the day I wrote this article, the JWKS looks like this:</p>
<pre>
$ curl -s https://api.apps.cam.ac.uk/oauth2/v1/.well-known/jwks.json
{
"keys": [
{
"alg": "RS256",
"e": "AQAB",
"n": "6kKjjctVPalX0ypJ2irwog8xIXS9JTABqrSnK_n3YJ4q0aH2-1bjGbWz8p1CaCUqDxQSDuqzvOgMdNGvZrxlNJ-G8hfc39jrb_KnB0T3ZsuxFz6X0mDzmHhdiPjSDK3M0syC4qg5_PB7xwKail5VWOcY0SypIYCPD6Ct5DGnQ_XONGXIVG7eaJAHdxJp2BOz0n3BVEFnZUgM5JcfGrSFfGqb0ZotX2AblwjZKQc58E0EVVykJw8gxW1Bob8rbaVXlMHssfY-9Jx0zua7ZrjO5C4OMmt9J6zYbVnGVwf62ehGtcLSP6iCG4_XM2sAMQwqJnPBss0U9WwDERk17FMHvb_FBwxAFxRygd0DclWmQmCYr5uFYck57KGARtyoxrNNAf4AFUHuObjbV24TyInYEgMhKi3SAML_4ke3dbbG-mjchXPN9OqNd4fydnQIP39WFHmFNk_nIlqvYnALI4xPE-w09T9jCvjU8hYHHlVMRvRluBnUzJkFnxLse5W-agC6ITe3wYvKH7SHVp6MYQWVD_0I2rCLV4gqjSpXzKIMs5eejjTQQq0VYumgL_f1ETvzDoewzXLOC8GGu2LZDwDbP0ea6DchReWjZfj4nJx23uQyGAj1h_uPI1jCd9oeJhbN8jFz2ltYgXYBp51qdSzsbtdec9BPPBVeXjI--c0AWU8",
"kid": "70e0ed3c",
"kty": "RSA",
"use": "sig"
}
]
}
</pre>
<p>They very kindly pretty-print it too!</p>
<p>We can then get the PEM version by piping to <tt>jwks-to-pem.py</tt>:</p>
<pre>
$ curl -s https://api.apps.cam.ac.uk/oauth2/v1/.well-known/jwks.json | jwks-to-pem.py
# Key 0 (kid: 70e0ed3c)
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA6kKjjctVPalX0ypJ2irw
og8xIXS9JTABqrSnK/n3YJ4q0aH2+1bjGbWz8p1CaCUqDxQSDuqzvOgMdNGvZrxl
NJ+G8hfc39jrb/KnB0T3ZsuxFz6X0mDzmHhdiPjSDK3M0syC4qg5/PB7xwKail5V
WOcY0SypIYCPD6Ct5DGnQ/XONGXIVG7eaJAHdxJp2BOz0n3BVEFnZUgM5JcfGrSF
fGqb0ZotX2AblwjZKQc58E0EVVykJw8gxW1Bob8rbaVXlMHssfY+9Jx0zua7ZrjO
5C4OMmt9J6zYbVnGVwf62ehGtcLSP6iCG4/XM2sAMQwqJnPBss0U9WwDERk17FMH
vb/FBwxAFxRygd0DclWmQmCYr5uFYck57KGARtyoxrNNAf4AFUHuObjbV24TyInY
EgMhKi3SAML/4ke3dbbG+mjchXPN9OqNd4fydnQIP39WFHmFNk/nIlqvYnALI4xP
E+w09T9jCvjU8hYHHlVMRvRluBnUzJkFnxLse5W+agC6ITe3wYvKH7SHVp6MYQWV
D/0I2rCLV4gqjSpXzKIMs5eejjTQQq0VYumgL/f1ETvzDoewzXLOC8GGu2LZDwDb
P0ea6DchReWjZfj4nJx23uQyGAj1h/uPI1jCd9oeJhbN8jFz2ltYgXYBp51qdSzs
btdec9BPPBVeXjI++c0AWU8CAwEAAQ==
-----END PUBLIC KEY-----
</pre>
<h3>The script</h3>
<p>This is the script in case anyone else finds it useful:</p>
<pre lang="python" id="script">
#!/usr/bin/env -S uv run --script --quiet
# /// script
# dependencies = [
# "cryptography",
# ]
# ///
"""Convert JWK keys to PEM format.
This script reads .well-known/jwks.json and outputs PEM encoded versions
of the public keys in that file.
Usage:
curl -s https://example.com/.well-known/jwks.json | jwks-to-pem.py
uv run jwks-to-pem.py jwks.json
uv run jwks-to-pem.py < jwks.json
Requirements:
- uv (https://github.com/astral-sh/uv)
- cryptography library
Author:
Rob Allen <rob@akrabat.com>
Copyright 2025
License:
MIT License - https://opensource.org/licenses/MIT
"""
import json
import base64
import sys
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
def base64url_decode(data):
"""Decode base64url to bytes"""
# Add padding if needed
padding = 4 - len(data) % 4
if padding != 4:
data += '=' * padding
# Replace URL-safe chars
data = data.replace('-', '+').replace('_', '/')
# Decode
return base64.b64decode(data)
def jwk_to_pem(jwk_key):
"""Convert JWK to PEM format"""
if jwk_key['kty'] != 'RSA':
raise ValueError("Only RSA keys are supported")
# Decode the modulus (n) and exponent (e) to int
n = int.from_bytes(base64url_decode(jwk_key['n']), 'big')
e = int.from_bytes(base64url_decode(jwk_key['e']), 'big')
# Create RSA public key
public_key = rsa.RSAPublicNumbers(e, n).public_key()
# Serialize to PEM
pem = public_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo
)
return pem.decode()
def main():
if len(sys.argv) > 2:
print("Usage: jwk_to_pem.py [jwks.json]")
print("If no file is provided, reads from stdin")
sys.exit(1)
if len(sys.argv) == 2 and sys.argv[1] != '-':
# Read from file
with open(sys.argv[1], 'r') as f:
jwks = json.load(f)
else:
# Read from stdin
jwks = json.load(sys.stdin)
# Convert each key
for i, key in enumerate(jwks['keys']):
kid = key.get('kid', f'key-{i}')
print(f"# Key {i} (kid: {kid})")
print(jwk_to_pem(key))
if __name__ == "__main__":
main()
</pre>
]]></content:encoded>
<wfw:commentRss>https://akrabat.com/converting-jwks-json-to-pem-using-python/feed/</wfw:commentRss>
<slash:comments>0</slash:comments>
</item>
<item>
<title>Stop in-place editing of bash history items</title>
<link>https://akrabat.com/stop-in-place-editing-of-bash-history-items/</link>
<comments>https://akrabat.com/stop-in-place-editing-of-bash-history-items/#comments</comments>
<dc:creator><![CDATA[Rob]]></dc:creator>
<pubDate>Tue, 02 Sep 2025 10:00:00 +0000</pubDate>
<category><![CDATA[Command Line]]></category>
<category><![CDATA[Shell Scripting]]></category>
<guid isPermaLink="false">https://akrabat.com/?p=7480</guid>
<description><![CDATA[Recently, since getting a new computer, I've noticed that I've been losing bash history items and it took a while to work out what was going on, though I'm still not completely sure as it never seemed to be so much of a problem. I regularly use the up and down keys with context specific history. For example, I will type ma and then press up to search back through all the make commands I've… <a href="https://akrabat.com/stop-in-place-editing-of-bash-history-items/">continue reading</a>.]]></description>
<content:encoded><![CDATA[<p>Recently, since getting a new computer, I've noticed that I've been losing bash history items and it took a while to work out what was going on, though I'm still not completely sure as it never seemed to be so much of a problem. </p>
<p>I regularly use the up and down keys with <a href="/context-specific-history-at-the-bash-prompt/">context specific history</a>. For example, I will type <tt>ma</tt> and then press up to search back through all the <tt>make</tt> commands I've used recently and then press enter to run it. </p>
<p>Sometimes, I'll realise that I don't actually want this command and edit it and press enter. Sometimes I'll decide halfway through editing that really I should use a <tt>docker compose</tt> command instead and I'll just back out of my edit via some key stroke that works. I'm not sure what I do here though, probably up/down, or maybe ctrl+c. Whatever I do, sometimes, the history for that line is now my edited mess and not the original command. Then later, when I go to try and find it via the up arrow, it's missing.</p>
<p>This happened infrequently enough that I thought I was misremembering what was in the history, or that maybe it was another tab I was thinking about.</p>
<p>I never want the bash history to be editable; if I cancel out, then I want it back to what it was.</p>
<h2>Fixing with revert-all-at-newline</h2>
<p>This finally annoyed me enough that I sat down with the Internet to work out how to fix it with the <tt>revert-all-at-newline</tt> setting.</p>
<p>The revert-all-at-newline option in bash controls whether readline reverts any changes made to a history line when you press Enter. Note that this is part of readline's behavior, so it affects command line editing in bash and other programs that use <a href="https://en.wikipedia.org/wiki/GNU_Readline">readline</a>.</p>
<p>The simplest thing is to add this to <tt>.bashrc</tt>:</p>
<pre lang="bash">
bind 'set revert-all-at-newline on'
</pre>
<p>Alternatively, you can create a <tt>.inputrc</tt> file with this in it:</p>
<pre>
$include /etc/inputrc
set revert-all-at-newline on
</pre>
<p>To view the current value of <tt>revert-all-at-newline</tt>, use: </p>
<pre>
bind -V | grep revert-all-at-newline
</pre>
<p>It solved my problem, and I've not yet found a case when I want it set the other way.</p>
]]></content:encoded>
<wfw:commentRss>https://akrabat.com/stop-in-place-editing-of-bash-history-items/feed/</wfw:commentRss>
<slash:comments>1</slash:comments>
</item>
<item>
<title>Extending an OpenAPI Component Schema</title>
<link>https://akrabat.com/extending-an-openapi-component-schema/</link>
<comments>https://akrabat.com/extending-an-openapi-component-schema/#respond</comments>
<dc:creator><![CDATA[Rob]]></dc:creator>
<pubDate>Tue, 26 Aug 2025 10:00:00 +0000</pubDate>
<category><![CDATA[OpenAPI]]></category>
<guid isPermaLink="false">https://akrabat.com/?p=7477</guid>
<description><![CDATA[One project that I'm working on uses RFC 9457 Problem Details for HTTP APIs for its error responses. In the OpenAPI spec, we can define this as a component and use in the relevant paths as appropriate: components: schemas: ProblemDetails: type: object properties: type: type: string format: uri-reference description: A URI reference that identifies the problem type default: about:blank example: https://example.com/probs/out-of-credit title: type: string description: A short, human-readable summary of the problem type example: You… <a href="https://akrabat.com/extending-an-openapi-component-schema/">continue reading</a>.]]></description>
<content:encoded><![CDATA[<p>One project that I'm working on uses <a href="https://www.rfc-editor.org/rfc/rfc9457.html">RFC 9457 Problem Details for HTTP APIs </a> for its error responses.</p>
<p>In the OpenAPI spec, we can define this as a component and use in the relevant paths as appropriate:</p>
<pre lang="yaml">
components:
schemas:
ProblemDetails:
type: object
properties:
type:
type: string
format: uri-reference
description: A URI reference that identifies the problem type
default: about:blank
example: https://example.com/probs/out-of-credit
title:
type: string
description: A short, human-readable summary of the problem type
example: You do not have enough credit.
status:
type: integer
format: int32
description: The HTTP status code for this occurrence of the problem
minimum: 100
maximum: 599
example: 403
detail:
type: string
description: A human-readable explanation specific to this occurrence of the problem
example: Your current balance is 30, but that costs 50.
instance:
type: string
format: uri-reference
description: A URI reference that identifies the specific occurrence of the problem
example: /account/12345/msgs/abc
additionalProperties: true
</pre>
<p>When we return a validation error, we add an <tt>errors</tt> property. Rather than repeating the <tt>ProblemDetails</tt> properties into <tt>ValidationError</tt>, we can add the <tt>errors</tt> using <a href="https://json-schema.org/understanding-json-schema/reference/combining#allOf"><tt>allOf</tt></a>:</p>
<pre lang="yaml">
ValidationError:
allOf:
- $ref: '#/components/schemas/ProblemDetails'
- type: object
properties:
errors:
type: object
description: Field-specific validation error messages
additionalProperties:
type: string
example:
name: "name must be provided"
dateOfBirth: "date must be in the past"
</pre>
<p>This is quite handy!</p>
]]></content:encoded>
<wfw:commentRss>https://akrabat.com/extending-an-openapi-component-schema/feed/</wfw:commentRss>
<slash:comments>0</slash:comments>
</item>
<item>
<title>Saving the current URL to a Note</title>
<link>https://akrabat.com/saving-the-current-url-to-a-note/</link>
<comments>https://akrabat.com/saving-the-current-url-to-a-note/#respond</comments>
<dc:creator><![CDATA[Rob]]></dc:creator>
<pubDate>Tue, 19 Aug 2025 10:00:00 +0000</pubDate>
<category><![CDATA[Mac]]></category>
<category><![CDATA[Shortcuts]]></category>
<guid isPermaLink="false">https://akrabat.com/?p=7467</guid>
<description><![CDATA[Inspired by John Gruber mentioning on the Cortex podcast that he has a shortcut that saves links to a note in Tot, I thought I'd do something similar for saving to a note in Apple Notes. I want to store as a bullet item containing the name of the page, the link and the date. Something like this: (Funny that the spellchecker doesn't know that Thu is the short form for Thursday) The Save Links… <a href="https://akrabat.com/saving-the-current-url-to-a-note/">continue reading</a>.]]></description>
<content:encoded><![CDATA[<p>Inspired by John Gruber mentioning on the <a href="https://www.relay.fm/cortex/169">Cortex podcast</a> that he has a shortcut that saves links to a note in <a href="https://tot.rocks">Tot</a>, I thought I'd do something similar for saving to a note in Apple Notes.</p>
<p>I want to store as a bullet item containing the name of the page, the link and the date. Something like this:</p>
<picture><source
srcset="https://akrabat.com/wp-content/uploads/2025/07/2025saved-link-text-dark.png"
media="(prefers-color-scheme: dark)"
/><source
srcset="https://akrabat.com/wp-content/uploads/2025/07/2025saved-link-text-light.png"
media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)"
/><br />
<img decoding="async" src="https://akrabat.com/wp-content/uploads/2025/07/2025saved-link-text-light.png" loading="lazy" alt="Saved link text light." class="border" width="408"/>
</picture>
<p>(Funny that the spellchecker doesn't know that Thu is the short form for Thursday)</p>
<h2>The <em>Save Links to Notes</em> Shortcut</h2>
<p>This is the shortcut that I created to do it:
<picture><source
srcset="https://akrabat.com/wp-content/uploads/2025/07/2025save-links-to-notes-shortcut-dark.png"
media="(prefers-color-scheme: dark)"
/><source
srcset="https://akrabat.com/wp-content/uploads/2025/07/2025save-links-to-notes-shortcut-light.png"
media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)"
/><br />
<img decoding="async" src="https://akrabat.com/wp-content/uploads/2025/07/2025save-links-to-notes-shortcut-light.png" loading="lazy" alt="Save links to notes shortcut light." class="noborder" width="558"/>
</picture>
<p>You can download it here:<br />
<a href="https://www.icloud.com/shortcuts/6c12e888f9da431d9977985b229d636b">https://www.icloud.com/shortcuts/6c12e888f9da431d9977985b229d636b</a></p>
<h2>Breaking down the actions</h2>
<p>To get the URL into the shortcut, we want:</p>
<ul>
<li><em>Show in Share Sheet</em> so that it's available on iOS/iPadOS</li>
<li><em>Receive What's Onscreen</em> so that when a browser is focussed on Mac, it finds the URL</li>
<li><em>Use as a Quick Action</em> so that we can assign a keyboard shortcut (<tt style="font-family: sans-serif">⌃⌥⌘U</tt> in case)</li>
</ul>
<p>We can then use <em>Get Contents of web page</em> along with <em>Get Details of Safari Web Page</em> to get the pages's title which Shortcuts calls <em>Name</em> for some reason.</p>
<p>There's an action for <em>Current Date</em>, so we add that to get the variable.</p>
<p>Creating formatted text in a note is a little involved. Firstly we use a <em>Text</em> action to set out the Markdown that we want. I used the date format <tt>EEE, dd MMM yyyy</tt> as it's short and clear to me.</p>
<p>There's a <em>Make Rich Text from Markdown</em> action which processes the Markdown for us, but if you just append it to the note, it doesn't work. The workaround is to add it to a <em>List</em> action and then append the list to the note. </p>
<h2>That's it</h2>
<p>All we need to do now is show a notification including the <tt>Shortcut Input</tt> variable as that's the URL that we've just saved.
<picture><source
srcset="https://akrabat.com/wp-content/uploads/2025/07/2025save-links-to-notes-notification-dark.png"
media="(prefers-color-scheme: dark)"
/><source
srcset="https://akrabat.com/wp-content/uploads/2025/07/2025save-links-to-notes-notification-light.png"
media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)"
/><br />
<img decoding="async" src="https://akrabat.com/wp-content/uploads/2025/07/2025save-links-to-notes-notification-light.png" loading="lazy" alt="Save links to notes notification light." class="noborder" width="376"/>
</picture>
<p>With this shortcut, I can add a new entry to my note from both my Mac, iPad and iPhone with minimal effort. </p>
<p>I like it.</p>
]]></content:encoded>
<wfw:commentRss>https://akrabat.com/saving-the-current-url-to-a-note/feed/</wfw:commentRss>
<slash:comments>0</slash:comments>
</item>
<item>
<title>Accessing Longplay info for SwiftBar</title>
<link>https://akrabat.com/accessing-longplay-info-for-swiftbar/</link>
<comments>https://akrabat.com/accessing-longplay-info-for-swiftbar/#respond</comments>
<dc:creator><![CDATA[Rob]]></dc:creator>
<pubDate>Tue, 12 Aug 2025 10:00:00 +0000</pubDate>
<category><![CDATA[Mac]]></category>
<guid isPermaLink="false">https://akrabat.com/?p=7447</guid>
<description><![CDATA[One app that I find incredibly useful is SwiftBar and one use I have is to display track info for the currently playing song in Apple Music. SwiftBar plugins work as shell scripts that execute on a timer and echo specially formatted text which SwiftBar then turns into an item on the menu bar with an attached menu I use a heavily modified Now Playing plugin that was originally written by Adam Kenyon, so all… <a href="https://akrabat.com/accessing-longplay-info-for-swiftbar/">continue reading</a>.]]></description>
<content:encoded><![CDATA[<p>One app that I find incredibly useful is <a href="https://github.com/swiftbar/SwiftBar">SwiftBar</a> and one use I have is to display track info for the currently playing song in Apple Music.</p>
<p>SwiftBar plugins work as shell scripts that execute on a timer and echo specially formatted text which SwiftBar then turns into an item on the menu bar with an attached menu</p>
<p>I use a heavily modified <a href="https://github.com/matryer/xbar-plugins/blob/main/Music/nowplaying.5s.sh">Now Playing plugin</a> that was originally written by Adam Kenyon, so all the hard work was done by them.</p>
<p>Recently, I've been using <a href="https://longplay.rocks">Longplay</a> to play albums and wanted the same functionality.</p>
<p>Now Playing uses AppleScript to determine if a music player is playing and what the track info is:</p>
<pre>
app_playing=$(osascript -e "tell application \"$i\" to player state as string")
</pre>
<p>And</p>
<pre>
track=$(osascript -e "tell application \"$app\" to name of current track")
artist=$(osascript -e "tell application \"$app\" to artist of current track")
</pre>
<p>When looking at adding Longplay, I was pleased to discover that it has AppleScript support, but perusing the Dictionary, I discovered that it doesn't support the features I need here.</p>
<p>Upon emailing the developer, they very helpfully pointed out that Longplay also has Shortcuts support and that I could probably use that instead. They were right.</p>
<p>I knocked up a couple of shortcuts:</p>
<picture><source
srcset="https://akrabat.com/wp-content/uploads/2025/07/2025longplay-status-dark.png"
media="(prefers-color-scheme: dark)"
/><source
srcset="https://akrabat.com/wp-content/uploads/2025/07/2025longplay-status-light.png"
media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)"
/><br />
<img decoding="async" src="https://akrabat.com/wp-content/uploads/2025/07/2025longplay-status-light.png" loading="lazy" alt="Longplay status Apple Shortcut" class="noborder" width="431"/>
</picture>
<p>and</p>
<picture><source
srcset="https://akrabat.com/wp-content/uploads/2025/07/2025longplay-now-playing-dark.png"
media="(prefers-color-scheme: dark)"
/><source
srcset="https://akrabat.com/wp-content/uploads/2025/07/2025longplay-now-playing-light.png"
media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)"
/><br />
<img decoding="async" src="https://akrabat.com/wp-content/uploads/2025/07/2025longplay-now-playing-light.png" loading="lazy" alt="Longplay now playing light." class="noborder" width="431"/>
</picture>
<p>With these set-up, I can now run them from the command line using <tt>shortcuts</tt>:</p>
<pre>
app_playing=$(shortcuts run "Longplay status");
</pre>
<p>This will set <tt>$app_playing</tt> to either "Yes" or "No" as strings as it is defined as boolean in Shortcuts.</p>
<pre>
track=$(shortcuts run "Longplay now playing");
</pre>
<p>This simply sets <tt>$track</tt> to the string of the currently playing track.</p>
<h2>Updated Now Playing script</h2>
<p>With the ability to get the info I needed from the command line, I <s>hacked</s> updated my copy of the Now Playing script and all is good.</p>
<p>I've updated it a bit over the years, so I've uploaded my version to Gist: <a href="https://gist.github.com/akrabat/8bcfac9dfef5fd4e9b67ac5bb504ea7a">nowplaying.5s.sh</a>.</p>
]]></content:encoded>
<wfw:commentRss>https://akrabat.com/accessing-longplay-info-for-swiftbar/feed/</wfw:commentRss>
<slash:comments>0</slash:comments>
</item>
<item>
<title>Responding to StreamDeck buttons with Keyboard Maestro</title>
<link>https://akrabat.com/controlling-the-streamdeck-via-keyboard-maestro/</link>
<comments>https://akrabat.com/controlling-the-streamdeck-via-keyboard-maestro/#respond</comments>
<dc:creator><![CDATA[Rob]]></dc:creator>
<pubDate>Tue, 05 Aug 2025 10:00:00 +0000</pubDate>
<category><![CDATA[AppleScript]]></category>
<category><![CDATA[Keyboard Maestro]]></category>
<category><![CDATA[Mac]]></category>
<guid isPermaLink="false">https://akrabat.com/?p=7440</guid>
<description><![CDATA[I run Apple Music on my Mac desktop and send the output to my HomePod minis. To control the volume, you need to manipulate the Apple Music volume slider rather than the global volume controls for the Mac. It's easier to press buttons than use a mouse, so I used Keyboard Maestro to respond to two buttons on my Stream Deck instead. This is possible because Keyboard Maestro has a Stream Deck Plugin, so you… <a href="https://akrabat.com/controlling-the-streamdeck-via-keyboard-maestro/">continue reading</a>.]]></description>
<content:encoded><![CDATA[<p>I run Apple Music on my Mac desktop and send the output to my HomePod minis. To control the volume, you need to manipulate the Apple Music volume slider rather than the global volume controls for the Mac.</p>
<p>It's easier to press buttons than use a mouse, so I used <a href="https://www.keyboardmaestro.com/">Keyboard Maestro</a> to respond to two buttons on my <a href="https://www.elgato.com/uk/en/p/stream-deck">Stream Deck</a> instead.</p>
<p>This is possible because Keyboard Maestro has a <a href="https://marketplace.elgato.com/product/keyboard-maestro-35c7590b-b7fb-4be0-9e5d-9fd4b4c0f013">Stream Deck Plugin</a>, so you need to install that first.</p>
<h2>Setting up the Stream Deck button</h2>
<p>You can now assign Keyboard Maestro to a button in the Stream Deck software:</p>
<p><img fetchpriority="high" decoding="async" src="https://akrabat.com/wp-content/uploads/2025/07/2025streamdeck-km-button.png" alt="Keyboard Maestro automation configuration interface showing a button setup with a speaker/volume icon. The interface displays fields for Title (empty), Button ID (R3C1), Virtual Row (3), and Virtual Column (1). The left side shows a black square button with white speaker and minus icons." title="streamdeck-km-button.png" border="0" width="500" height="239" /></p>
<p>This is the configuration for my volume down button, as you can tell by the icon I chose. The Button ID defaults to the row and column number of where you have placed it on the Stream Deck.</p>
<h2>Responding to the button in Keyboard Maestro</h2>
<p>On the Keyboard Maestro side, we need a macro that is trigged by the Stream Deck button. This is easy to do as it looks like a USB device key and you can press the button the Stream Deck and Keyboard Maestro will recognise it and fill in the correct details.</p>
<p><img decoding="async" src="https://akrabat.com/wp-content/uploads/2025/07/2025streamdeck-km-control.png" alt="Screenshot of an Keyboard Maestro automation interface showing a "Music volume down" macro. The trigger is a Stream Deck R3C1 button press with modifiers. The action executes AppleScript code that decreases the Music app's volume by 1, with a minimum volume of 0." title="streamdeck-km-control.png" border="0" width="500" height="529" /></p>
<p>Upon clicking the button, we simply run some AppleScript to control the Music app's volume.</p>
<h2>That's it</h2>
<p>That's all there is to responding to a button on the Stream Deck on a Mac. In this case, I'm using AppleScript, but Keyboard Maestro lets you do practically anything on the computer!</p>
]]></content:encoded>
<wfw:commentRss>https://akrabat.com/controlling-the-streamdeck-via-keyboard-maestro/feed/</wfw:commentRss>
<slash:comments>0</slash:comments>
</item>
<item>
<title>Step-debugging Docker Compose NestJS services</title>
<link>https://akrabat.com/step-debugging-docker-compose-nestjs-services/</link>
<comments>https://akrabat.com/step-debugging-docker-compose-nestjs-services/#respond</comments>
<dc:creator><![CDATA[Rob]]></dc:creator>
<pubDate>Tue, 29 Jul 2025 10:00:00 +0000</pubDate>
<category><![CDATA[NodeJS]]></category>
<category><![CDATA[TypeScript]]></category>
<guid isPermaLink="false">https://akrabat.com/?p=7454</guid>
<description><![CDATA[I'm working on a NestJS project that uses monorepo mode. It consists of a number of separate microservice applications that each have their own Docker container that are managed in development using Docker Compose. I like step-debugging in my IDE and so needed to set it up for this application. This is what I did. The application setup Each service in our project has its own container in docker-compose.yaml and we use a reverse proxy to… <a href="https://akrabat.com/step-debugging-docker-compose-nestjs-services/">continue reading</a>.]]></description>
<content:encoded><![CDATA[<p>I'm working on a <a href="https://nestjs.com">NestJS</a> project that uses <a href="https://docs.nestjs.com/cli/monorepo#monorepo-mode">monorepo mode</a>. It consists of a number of separate microservice applications that each have their own <a href="https://www.docker.com">Docker</a> container that are managed in development using <a href="https://docs.docker.com/compose/">Docker Compose</a>.</p>
<p>I like step-debugging in my IDE and so needed to set it up for this application. This is what I did.</p>
<h2>The application setup</h2>
<p>Each service in our project has its own container in docker-compose.yaml and we use a reverse proxy to have a single endpoint that routes requests to the correct microservice. Each custom <tt>Dockerfile</tt> runs <tt>ppm run start:dev:{service name}></tt> which is defined in <tt>package.json</tt> like this:</p>
<pre>
"start:dev:service1": "nest start service1 --watch --tsc --watchOptions.poll=1000 --preserveWatchOutput",
</pre>
<p>Using <tt>--watchOptions.poll=1000</tt> is just more reliable when running in Docker with volumes mounted into the container. We also set the <tt>--preserveWatchOutput</tt> flag to ensure that the service doesn't take control of the terminal as this is unhelpful when you have multiple services in play.</p>
<h2>Set the apps up for debugging</h2>
<p>We need to make some modification for step debugging. Firstly, I created a set of <tt>start:debug:{service name}</tt> scripts in <tt>package.json</tt> that look like this:</p>
<pre>
"start:debug:service1": "nest start service1 --debug 0.0.0.0:9229 --watch --tsc --watchOptions.poll=1000 --preserveWatchOutput",
</pre>
<p>We enabled the <tt>--debug</tt> flag to enable node's <tt>--inspect</tt> flag so that port <tt>9229</tt> is available to the debugger. However, buy default this is bound to <tt>127.0.0.1</tt> which is not useful in a container, so we bind to <tt>0.0.0.0:9229</tt> so that it's available outside the container.</p>
<p>Next, we need to our new <tt>start:debug:{service name}</tt> scripts and expose port 92229 to our local environment for each service. We do this in <tt>docker-compose.override.yaml</tt>:</p>
<pre lang="yaml">
services:
service1:
ports:
- "9230:9229"
command: pnpm run start:debug:service1
service2:
ports:
- "9231:9229"
command: pnpm run start:debug:service2
</pre>
<p>I don't tend to like binding to the default port as that invariably confuses me when I run some test thing locally, so I've picked ports starting from <tt>9230</tt> onwards for my services.</p>
<p>Running <tt>docker compose up</tt> will now start the containers.</p>
<h2>Debugging in WebStorm</h2>
<p>To set up <a href="https://www.jetbrains.com/webstorm/">WebStorm</a> for step debugging, create a <em>Run/Debug configuration</em> entry of type <tt>Attach to Node.js/Chrome</tt> for each container.</p>
<p>The settings for service1 are:</p>
<ul>
<li>Name: Debug service1</li>
<li>Host: localhost</li>
<li>Port: 9230</li>
</ul>
<p>For the other services, change the name and port.</p>
<picture><source
srcset="https://akrabat.com/wp-content/uploads/2025/07/2025webstorm-nodejs-debug-config-dark.png"
media="(prefers-color-scheme: dark)"
/><source
srcset="https://akrabat.com/wp-content/uploads/2025/07/2025webstorm-nodejs-debug-config-light.png"
media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)"
/><br />
<img decoding="async" src="https://akrabat.com/wp-content/uploads/2025/07/2025webstorm-nodejs-debug-config-light.png" loading="lazy" alt="Webstorm nodejs debug config light." class="noborder" width="600"/>
</picture>
<p>From the Debugging dropdown at in the title bar select the service and press the green "bug" button. You'll see a "Debugger attached." message in the Docker logs.</p>
<p>Attach a breakpoint and access the endpoint using <tt>curl</tt> or another HTTP client and the IDE should stop execution at the breakpoint.</p>
<h2>Debugging in VS Code</h2>
<p>To set up <a href="https://code.visualstudio.com">VS Code</a> for step debugging, create a <tt>.vscode/launch.json</tt> file in your project. It should look like this:</p>
<pre lang="json">
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug service1",
"type": "node",
"request": "attach",
"port": 9230,
"address": "localhost",
"localRoot": "${workspaceFolder}",
"remoteRoot": "/usr/src/app",
"restart": true
},
{
"name": "Debug service2",
"type": "node",
"request": "attach",
"port": 9231,
"address": "localhost",
"localRoot": "${workspaceFolder}",
"remoteRoot": "/usr/src/app",
"restart": true
}
]
}
</pre>
<p>Set <tt>remoteRoot</tt> to the directory within the Docker container where the project is mounted.</p>
<p>Start debugging by selecting the <em>Run and Debug</em> pane in the left hand toolbar and choose the service from the dropdown at the top. Then press the green <em>Start debugging</em> button (or press F5).You'll see a "Debugger attached." message in the Docker logs.</p>
<p>Attach a breakpoint and access the endpoint using <tt>curl</tt> or another HTTP client and the IDE should stop execution at the breakpoint.</p>
<h2>That's it</h2>
<p>Having done this, I can now enjoy step debugging this new-to-me codebase and understand what it does!</p>
]]></content:encoded>
<wfw:commentRss>https://akrabat.com/step-debugging-docker-compose-nestjs-services/feed/</wfw:commentRss>
<slash:comments>0</slash:comments>
</item>
</channel>
</rss>
If you would like to create a banner that links to this page (i.e. this validation result), do the following:
Download the "valid RSS" banner.
Upload the image to your own server. (This step is important. Please do not link directly to the image on this server.)
Add this HTML to your page (change the image src
attribute if necessary):
If you would like to create a text link instead, here is the URL you can use:
http://www.feedvalidator.org/check.cgi?url=http%3A//akrabat.com/feed/