23 Mar 2025 · 9 min read
Locking Notes in Obsidian
Over the course of last year I saw several mentions on social media and other online posts about Obsidian, the personal knowledge management application, not being able to lock posts. I had an idea about this at the time and finally got around to picking it off my to do list. In this post I’m going to outline something that I believe is at least a workable partial solution for this.
The Basic Premise
Locking a note is simply amending it so that you cannot change its contents. While I agree you can’t totally lock a note in Obsidian, you can lock its contents in most if not all operating systems. That’s generally quite straight forward to do in any operating system that provides scripting, so certainly desktop operating systems. The mileage for being able to do this likely varies on mobile operating systems. Note also that your choice of sync solution might well affect how the attributes and/or permissions are transferred.
Like I said I think this forms at least a partial solution, but I strongly suspect it would not be a solution that would work for all platforms and sync options.
For the remainder of this post I am going to focus purely on setting up something workable on macOS.
Toggling File Writability
macOS uses Unix-file permissions and we can utilise a couple of shell commands to change if a file is writable or not by the current user.
chmod +w note.mdwill make the note file writeable.chmod -w note.mdwill make the note file read only.
We can even create a little script to toggle this.
#!/bin/zsh
# The first argument of this script must be the path to the note file
if [[ -w "$1" ]]; then
chmod -w "$1"
else
chmod +w "$1"
fi
Triggering Scripts From Obsidian
My goto for triggering scripts and applications from Obsidian is via the Templater plugin. You can read more about this plugin in my basics of Templater for Obsidian post). I’ve used this a few times, and my Path-based Commands in Obsidian post covers the details about this.
In my testing it worked great … for writeable files. However Obsidian didn’t seem to let Templater trigger properly if the file was read only.
While Templater will likely remain a goto for me, it did lead me to investigating if anyone had created any other plugins that might give me a more direct option to trigger an external script. And like with most things Obsidian, yes, there is a plugin for that.
The plugin I quickly settled on was the aptly named Script Launcher (View in Obsidian) by Allessandro Ruggiero. This plugin allowed me to easily run the script
Visual Changes
Apart from messages in Obsidian about failing to write the note if you try and modify it, I figured it would be useful to have a visual update to the note if it was set as read only.
Obsidian has a front matter item called cssclasses which is a list of entries that correspond to custom CSS files you can store in Obsidian. If a note has an entry, the matching CSS file is loaded and applied.
So when setting a file to read only, it seemed sensible to add a readonly entry to cssclasses before setting the file’s properties to make it read only. Similarly, after making it writeable again, the readonly entry could be removed.
Obsidian notes are Markdown files and so they are just plain text. Fortunately that means that manipulating the files through shell scripting is relatively straight forward. It is not entirely free of complexity as not all files have cssclasses, and those that do might have other CSS to be applied in addition to the readonly CSS.
Setup
First of all, make sure you have Community Plugins enabled and the Script Launcher plugin installed and enabled in your vault. You may also wish to try this out on a test vault or ensure you have a good backup to draw on. My testing went well, but it is good practice to ensure you are not going to lose anything when running other people’s scripts like this.
Next, use your favourite text editor to create a new script file. For this example I’ve created it in my Documents folder, and named it togglero.sh, but you should feel free to modify this to suit your own setup.
The content to put in the file is given below. Note it is commented so please do read through and ensure you are happy with what it does before you use it.
#!/bin/zsh
# Get file content into a variable
NOTE_PATH="$1/${@:2}"
CONTENT=$(<"$NOTE_PATH")
info ()
{
# Just for debugging purposes
#say "$1"
#echo "$1"
echo "$1">/dev/null
}
addreadonly ()
{
info "Starting add read only function"
# Check if front matter exists
if [[ -f "$NOTE_PATH" ]] && [[ "$(head -n 1 "$NOTE_PATH")" == "---" ]]; then
info "Existing front matter found"
else
# No front matter, add it with cssclasses: readonly
info "Adding front matter"
echo "---\ncssclasses:\n - readonly\n---\n$CONTENT" > "$NOTE_PATH"
return
fi
# We have front matter - do we have any pre-existing cssclasses?
CSSCLASSES=$(awk '/^cssclasses:/ {print $0}' "$NOTE_PATH")
if [[ -z "$CSSCLASSES" ]]; then
# No cssclasses entry, so let's add it to the top of the front matter
info "No 'cssclasses' entry in front matter - adding entry"
awk 'NR==1 && /^---$/ {print; print "cssclasses:\n - readonly"; next} 1' "$NOTE_PATH" > "$NOTE_PATH.tmp" && mv "$NOTE_PATH.tmp" "$NOTE_PATH"
return
fi
# We have front matter with `cssclasses`, so let's toggle any readonly entry
togglecssclassesreadonly
}
removereadonly ()
{
info "Starting remove read only function"
# Match any pre-existing cssclasses
CSSCLASSES=$(awk '/^cssclasses:/ {print $0}' "$NOTE_PATH")
if [[ -z "$CSSCLASSES" ]]; then
# No cssclasses entry, so we don't need to update anything
return
fi
# We have front matter with `cssclasses`, so let's toggle any readonly entry
togglecssclassesreadonly
}
togglecssclassesreadonly ()
{
info "Starting toggle cssclasses read only function"
# Toggle 'readonly' in cssclasses while preserving YAML list format
if grep -q '^ - readonly' "$NOTE_PATH"; then
# Remove 'readonly'
info "Removing readonly"
sed -i '' '/^ - readonly$/d' "$NOTE_PATH"
else
# Add 'readonly' to the existing cssclasses list
info "Adding readonly"
sed -i '' '/cssclasses:/a\
- readonly\
' "$NOTE_PATH"
fi
}
# Main script
if [[ -w "$NOTE_PATH" ]]; then
# The file is writable - set it to read only
info "File is writable. Setting to read only."
info "First add a readonly entry for cssclasses in the front matter"
addreadonly
info "Setting file attribute to be non-writeable"
chmod -w "$NOTE_PATH"
else
# The file is read only - set it to writeable
info "File is read only Setting to writeable."
info "Setting file attribute to be writeable"
chmod +w "$NOTE_PATH"
info "Now remove any readonly entry for cssclasses in the front matter"
removereadonly
fi
info "Process Complete"
Next, ensure your script is executable. For my file I executed the command chmod +x ~/Documents/togglero.sh at the command line.
With the script in place, the next thing to do is to create the CSS file we want to use when the note is set to be read only.
Press CMD + . in Obsidian to open the settings, select Appearance in the left navigation, and then in the CSS Snippets section click on the folder icon to open your CSS snippet folder. This should be in the .obsidian/snippets sub directory in your selected Obsidian vault.
Again, using your test editor of choice, create a new file in this folder. This file should be called readonly.css. The name of this file is important as it is used within the script set out above. It is also a logical name, so I doubt you would have any conflicts or other need to change this unless you wish to localise it to another language.
The content I have used for the file is as follows:
.readonly
{
position: relative;
}
.readonly::before
{
content: "READ ONLY";
position: sticky;
top: 0;
left: 0;
right: 0;
display: block;
width: 100%;
height: 20px;
background-color: #ff0000;
color: white;
font-size: 12px;
font-weight: bold;
text-align: center;
line-height: 20px;
z-index: 100;
}
Now the CSS file is created, go back to the CSS Snippets settings in Obsidian. If you don’t see readonly.css, select the refresh icon. Once you do see it, enable it.

With our external files in place, we want to set up the trigger. Staying in Obsidian’s settings, navigate to Script Launcher in the Community Plugins section at the bottom of the list. Select to Add a Script if you are not presented with an empty set of fields. Then we want to enter a set of details into the settings for the script to launch.
Here are the settings I used. You can use whatever you like, but ensure your script path points to the script created earlier.
| Setting | Value |
|---|---|
| Script name | Toggle Read Only |
| Script path | /Users/stephen/Documents/togglero.sh |
| Show on bottom bar | Enabled |
| Run on startup | Disabled |
| Show exit code | Disabled |
| Icon | 🔑 |
It should look something like this when you enter the details.

Usage
This example note below already includes some cssclasses entries as well as some --- entries in places other delimiting than the front matter. This covered a few of my test cases (along with no cssclasses to begin with), and gives you a sense of what it works with.

Note that at the bottom of the window in the corner is a key icon - this is the trigger for the script - I figured a key could represent locking and unlocking a file, but you can choose whatever icon makes sense to you.
When the key is clicked, the script is executed and after a moment the readonly entry should appear in the cssclasses, and assuming you are in live preview mode, the readonly css will be applied. This presents as a red banner with white text saying “READ ONLY” at the top of the page. Note that for longer pages, this should remain visible as you scroll down the page.

Clicking the key icon again will revert to the earlier look, without the readonly entry in cssclasses.
But does this actually result in the note being toggled between being writable and being read only? Well, if you try and edit the note in any way, you should see a message like this.

At this point Obsidian will start getting upset with you. You can clear the messages by clicking on them, but at that point you have invalidated the tab the note is open in. You should open a new tab and close the original one. If you load the note again, you will see it remains unchanged.
Conclusion
Hopefully this gives at least some people out there a workable solution. It can certainly be ported to other desktop platforms, but as noted your choice of sync service and mobile platform could have an impact here. The aim of this post was simply to outline an approach and make it at least mostly practical for Mac users.
I hope you found this useful, and if you did, be sure to check out some of my other posts on Obsidian.