<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Edris</title>
    <description>The latest articles on DEV Community by Edris (@edrisranjbar).</description>
    <link>https://dev.to/edrisranjbar</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F164232%2Fe569d50c-e711-4f21-aabc-98a2f9bed9c9.jpeg</url>
      <title>DEV Community: Edris</title>
      <link>https://dev.to/edrisranjbar</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/edrisranjbar"/>
    <language>en</language>
    <item>
      <title>Edi Life OS v1.2: accounts, a one-command Docker install, and my first outside contributors</title>
      <dc:creator>Edris</dc:creator>
      <pubDate>Mon, 19 Oct 2026 10:04:18 +0000</pubDate>
      <link>https://dev.to/edrisranjbar/edi-life-os-v12-accounts-a-one-command-docker-install-and-my-first-outside-contributors-2p8l</link>
      <guid>https://dev.to/edrisranjbar/edi-life-os-v12-accounts-a-one-command-docker-install-and-my-first-outside-contributors-2p8l</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffocegrko6an235rtwtv1.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffocegrko6an235rtwtv1.gif" alt="Edi Life OS: tick a habit, ask Claude to log an expense, see it in Finance" width="600" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Less than two weeks after launching &lt;a href="https://github.com/edrisranjbar/lifeos" rel="noopener noreferrer"&gt;Edi Life OS&lt;/a&gt;, my self-hosted life dashboard (habits, goals, focus timer, Kanban, finances, plus an MCP server for AI assistants), here is v1.2. More than twenty pull requests have landed since the last release, and most of them came from people I had never met.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's new
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Multiple accounts with private workspaces
&lt;/h3&gt;

&lt;p&gt;Each account signs in on its own and has its own Finance, Focus, Goals, Growth, Kanban, habits, notes, attachments, theme and currency. Everything in the app and the API is filtered to the signed-in account, so one account cannot read another's data by guessing an ID.&lt;/p&gt;

&lt;p&gt;Two things to know before you use it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Every account is an administrator.&lt;/strong&gt; Administrators can reset each other's passwords, which means they can sign in as that person. There is no public sign-up; only add people you trust.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Back up your database before upgrading.&lt;/strong&gt; Version 1.2 migrates the schema (version 3), and older versions of the app must not run against it, because their queries would show every account's rows. The details are in &lt;a href="https://github.com/edrisranjbar/lifeos/blob/main/docs/accounts.md" rel="noopener noreferrer"&gt;docs/accounts.md&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  A prebuilt Docker image
&lt;/h3&gt;

&lt;p&gt;No clone, no build. The image is published for &lt;code&gt;amd64&lt;/code&gt; and &lt;code&gt;arm64&lt;/code&gt;, so it also runs on a Raspberry Pi:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSLO&lt;/span&gt; https://raw.githubusercontent.com/edrisranjbar/lifeos/main/docker-compose.image.yml
curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/edrisranjbar/lifeos/main/.env.example &lt;span class="nt"&gt;-o&lt;/span&gt; .env   &lt;span class="c"&gt;# then set your sign-in and passwords&lt;/span&gt;
docker compose &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose.image.yml up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;http://localhost:8080&lt;/code&gt;. Upgrade later with &lt;code&gt;docker compose -f docker-compose.image.yml pull &amp;amp;&amp;amp; docker compose -f docker-compose.image.yml up -d&lt;/code&gt;; your data lives in named volumes. Pin a version with &lt;code&gt;LIFEOS_VERSION=1.2.0&lt;/code&gt; in &lt;code&gt;.env&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  And more
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;18 more display currencies&lt;/strong&gt; (SEK, NOK, DKK, PLN, CZK, BRL, MXN, ARS, CLP, COP, SAR, QAR, KWD, EGP, ZAR, NGN, PKR, BDT). They only change the label; nothing is converted.&lt;/li&gt;
&lt;li&gt;Inline editing of goal checklist tasks, and redesigned Notes, Notepad and Personal Growth views.&lt;/li&gt;
&lt;li&gt;Hardening: logout is a POST with a CSRF check, schema changes run once per version under a lock, and static assets are cached with versioned URLs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The community part
&lt;/h2&gt;

&lt;p&gt;This is the part I did not expect. A set of small, well-explained issues (an accessibility fix, a few currencies, backup commands, reverse-proxy examples, a troubleshooting guide, a Spanish README) turned into pull requests from three people who had never contributed here before. Their work is in the release notes with their handles. If you want a first open-source contribution, the &lt;a href="https://github.com/edrisranjbar/lifeos/labels/good%20first%20issue" rel="noopener noreferrer"&gt;good first issues&lt;/a&gt; are real and there are more.&lt;/p&gt;

&lt;h2&gt;
  
  
  A bug I caught after publishing
&lt;/h2&gt;

&lt;p&gt;Right after I published the image I looked at the registry's tag list instead of assuming it was fine. The compose file asked for the &lt;code&gt;latest&lt;/code&gt; tag, and &lt;code&gt;latest&lt;/code&gt; did not exist yet, because that tag is only created when you push a release. The one-line install would have failed for the first person who tried it.&lt;/p&gt;

&lt;p&gt;I fixed the default, tagged the release, and then added a CI job that runs the README's exact steps on a clean machine (download the compose file, set a password, start the published image, sign in) on every change. If you publish an artifact, test the artifact, not just the code that builds it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Release notes and download: &lt;a href="https://github.com/edrisranjbar/lifeos/releases/tag/v1.2.0" rel="noopener noreferrer"&gt;https://github.com/edrisranjbar/lifeos/releases/tag/v1.2.0&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Repo: &lt;a href="https://github.com/edrisranjbar/lifeos" rel="noopener noreferrer"&gt;https://github.com/edrisranjbar/lifeos&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Landing page: &lt;a href="https://edrisranjbar.github.io/lifeos/" rel="noopener noreferrer"&gt;https://edrisranjbar.github.io/lifeos/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Docker image: &lt;code&gt;ghcr.io/edrisranjbar/lifeos&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What do you check before trusting a self-hosted project with your data? I'd like to hear it in the comments.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>selfhosted</category>
      <category>docker</category>
      <category>php</category>
    </item>
    <item>
      <title>Edi Life OS v1.1: what a public security pass changed after launching on Hacker News</title>
      <dc:creator>Edris</dc:creator>
      <pubDate>Sat, 10 Oct 2026 14:48:40 +0000</pubDate>
      <link>https://dev.to/edrisranjbar/edi-life-os-v11-what-a-public-security-pass-changed-after-launching-on-hacker-news-1l57</link>
      <guid>https://dev.to/edrisranjbar/edi-life-os-v11-what-a-public-security-pass-changed-after-launching-on-hacker-news-1l57</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffocegrko6an235rtwtv1.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffocegrko6an235rtwtv1.gif" alt="Edi Life OS demo: tick a habit, ask Claude to log an expense, see it in Finance" width="600" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A little while ago I posted &lt;a href="https://github.com/edrisranjbar/lifeos" rel="noopener noreferrer"&gt;Edi Life OS&lt;/a&gt; on Hacker News. It's a self-hosted dashboard for habits, goals, focus, money and projects, written in plain PHP, MySQL and vanilla JavaScript, with an MCP server so Claude and other assistants can work with your real data.&lt;/p&gt;

&lt;p&gt;The launch brought a lot more eyes than I expected. More people looking at the code meant more people who would actually install it on their own servers, so the first release after launch had one theme: &lt;strong&gt;trust&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Here's what changed in v1.1, and what I learned doing it.&lt;/p&gt;

&lt;h2&gt;
  
  
  A security pass, written down in public
&lt;/h2&gt;

&lt;p&gt;I went through the PHP backend and the frontend JavaScript and wrote every finding into &lt;a href="https://github.com/edrisranjbar/lifeos/issues/19" rel="noopener noreferrer"&gt;one public issue&lt;/a&gt;, ordered by priority. Then I fixed them from the top.&lt;/p&gt;

&lt;h3&gt;
  
  
  High
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Stored XSS on the dashboard.&lt;/strong&gt; Goal titles, categories, metrics and finance category names were rendered with &lt;code&gt;innerHTML&lt;/code&gt; without escaping. Those values can also be written through the bearer-token API, so API data could run script in the signed-in session. They're now escaped.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open redirect after sign-in.&lt;/strong&gt; The &lt;code&gt;redirect&lt;/code&gt; parameter only had to start with &lt;code&gt;/&lt;/code&gt; and not &lt;code&gt;//&lt;/code&gt;, which browsers can be tricked around. It now has to be a real same-origin path.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Medium
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CSRF on Habittify.&lt;/strong&gt; Every write endpoint checked a CSRF token except the habit tracker's API. Now it does too, through one shared &lt;code&gt;life_os_require_csrf()&lt;/code&gt; helper.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sign-in rate limiting.&lt;/strong&gt; After 5 failed attempts from one client in 15 minutes you get &lt;code&gt;429&lt;/code&gt; with &lt;code&gt;Retry-After&lt;/code&gt;, and the password isn't even checked. The limit is per IP (stored as a SHA-256 hash), not per username, so an attacker can't lock the owner out from everywhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security headers.&lt;/strong&gt; &lt;code&gt;Content-Security-Policy&lt;/code&gt;, &lt;code&gt;X-Frame-Options: SAMEORIGIN&lt;/code&gt;, &lt;code&gt;Referrer-Policy&lt;/code&gt; and &lt;code&gt;X-Content-Type-Options&lt;/code&gt; on every response. The CSP still allows inline scripts for now, because the app has inline handlers; moving them out is next.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTPS behind a reverse proxy.&lt;/strong&gt; A new &lt;code&gt;LIFEOS_TRUST_PROXY&lt;/code&gt; setting makes the session cookie &lt;code&gt;Secure&lt;/code&gt; based on &lt;code&gt;X-Forwarded-Proto&lt;/code&gt; and rate-limits by the real client IP. It's off by default, because trusting those headers on a directly exposed app would let anyone spoof them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The lesson:&lt;/strong&gt; with no framework, every protection a framework gives you for free (escaping, CSRF, headers) has to be built on purpose and applied everywhere. One endpoint that was written a little earlier than the others is enough to leave a gap.&lt;/p&gt;

&lt;h2&gt;
  
  
  A leaner MCP server
&lt;/h2&gt;

&lt;p&gt;The MCP server never touches the database. Every tool goes through the same token-protected HTTP API the app uses, delete tools are marked destructive so the client asks first, and secret notes stay hidden unless the server explicitly allows them.&lt;/p&gt;

&lt;p&gt;In v1.1, the 12 read-only tools became &lt;strong&gt;one &lt;code&gt;lifeos_query&lt;/code&gt; tool&lt;/strong&gt; with a &lt;code&gt;type&lt;/code&gt; parameter (&lt;code&gt;dashboard&lt;/code&gt;, &lt;code&gt;goals&lt;/code&gt;, &lt;code&gt;tasks&lt;/code&gt;, &lt;code&gt;habits&lt;/code&gt;, &lt;code&gt;finance_summary&lt;/code&gt;, &lt;code&gt;notes&lt;/code&gt; and so on). Fewer tools means less of every conversation spent on tool descriptions, and an easier choice for the model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Also new
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Display currency.&lt;/strong&gt; Finance started out Toman-only. You can now pick USD, EUR, GBP, AED and others in Settings. Amounts are labels and are never converted.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A landing page:&lt;/strong&gt; &lt;a href="https://edrisranjbar.github.io/lifeos/" rel="noopener noreferrer"&gt;edrisranjbar.github.io/lifeos&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;First outside contributions:&lt;/strong&gt; CI that runs the PHP and Node tests on every PR, a Docker health check, and light-theme screenshots.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/edrisranjbar/lifeos.git &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;lifeos
&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env   &lt;span class="c"&gt;# change the username and passwords&lt;/span&gt;
docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;   &lt;span class="c"&gt;# then open http://localhost:8080&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's also a release zip for shared hosting: extract it, add your MySQL details, done.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Repo: &lt;a href="https://github.com/edrisranjbar/lifeos" rel="noopener noreferrer"&gt;https://github.com/edrisranjbar/lifeos&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Release notes: &lt;a href="https://github.com/edrisranjbar/lifeos/releases/tag/v1.1.0" rel="noopener noreferrer"&gt;https://github.com/edrisranjbar/lifeos/releases/tag/v1.1.0&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you self-host things: what's the first thing you check before you install an open-source app on your server? I'd love to hear it in the comments.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>php</category>
      <category>security</category>
      <category>mcp</category>
    </item>
    <item>
      <title>Building a self-hosted life OS that my AI assistant can actually use</title>
      <dc:creator>Edris</dc:creator>
      <pubDate>Thu, 08 Oct 2026 08:05:45 +0000</pubDate>
      <link>https://dev.to/edrisranjbar/building-a-self-hosted-life-os-that-my-ai-assistant-can-actually-use-m7n</link>
      <guid>https://dev.to/edrisranjbar/building-a-self-hosted-life-os-that-my-ai-assistant-can-actually-use-m7n</guid>
      <description>&lt;p&gt;My days used to live in five places: a to-do app, a habit tracker, a budgeting spreadsheet, a Pomodoro timer and a notes app. Each one was fine on its own. None of them knew about the others, so the question I actually cared about ("am I moving toward the life I want?") had no single place to live.&lt;/p&gt;

&lt;p&gt;So I built one. &lt;strong&gt;Edi Life OS&lt;/strong&gt; is a self-hosted dashboard for focus, habits, goals, projects and money, and it ships with an MCP server so an AI assistant like Claude can work with my real data. It's open source under the MIT license: &lt;a href="https://github.com/edrisranjbar/lifeos" rel="noopener noreferrer"&gt;github.com/edrisranjbar/lifeos&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This post is about the decisions behind it, including a few that were less obvious than I expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's in it
&lt;/h2&gt;

&lt;p&gt;Ten workspaces share one design, one sign-in and one database:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Overview&lt;/strong&gt;: a daily score built from focus time, habit consistency, goal progress and finished work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Growth&lt;/strong&gt;: six life areas (spirituality, health, relationships and family, finance, self-growth, fun and rest), each linked to long-term goals, SMART goals, habits and tasks, with guided weekly, monthly and quarterly reviews.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Focus&lt;/strong&gt;: a Pomodoro timer with a built-in soundtrack.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Habittify&lt;/strong&gt;, &lt;strong&gt;Goals&lt;/strong&gt;, &lt;strong&gt;Kanban&lt;/strong&gt;, &lt;strong&gt;Calendar&lt;/strong&gt;, &lt;strong&gt;Notepad&lt;/strong&gt;, &lt;strong&gt;Notes&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Finance&lt;/strong&gt;: expenses, income and budgets, plus debts and credits (one-time or recurring) that write straight into the ledger.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The point isn't the number of features. It's that they're connected: a habit can drive a goal's progress, a Kanban card shows up in the calendar, and the Overview turns all of it into one picture of the day.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq0xla3gs2tawq70rf52k.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq0xla3gs2tawq70rf52k.webp" alt="Growth workspace: six life dimensions with SMART progress" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Growth: six life areas, each with its own progress, habits and tasks.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F49g0w5th7e5t0eitimqc.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F49g0w5th7e5t0eitimqc.webp" alt="Habittify: daily habit checklist with a seven-day chart" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Habittify: small daily habits and a seven-day picture of consistency.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2vk6f8haaqdnmoxm0qe6.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2vk6f8haaqdnmoxm0qe6.webp" alt="Kanban board with backlog, in progress, review and done columns" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Kanban: labels, due dates and checklists.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2ja592pgj3ne01azvwbb.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2ja592pgj3ne01azvwbb.webp" alt="Focus timer over an illustrated landscape" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Focus: a Pomodoro timer with a soundtrack and a landscape that follows the time of day.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Boring stack on purpose
&lt;/h2&gt;

&lt;p&gt;It's plain PHP 8, MySQL and vanilla JavaScript. No framework, no frontend build step.&lt;/p&gt;

&lt;p&gt;That was a deliberate choice. I wanted something that runs on cheap shared hosting as easily as on a server, that I can still read in five years, and that has almost nothing to upgrade. Each workspace is a page; there is no bundler.&lt;/p&gt;

&lt;p&gt;There are two ways to install it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;docker compose up -d&lt;/code&gt; with a &lt;code&gt;.env&lt;/code&gt; file (PHP 8.3 + Apache and MySQL 8.4, with volumes for the database and attachments).&lt;/li&gt;
&lt;li&gt;A release zip that matches cPanel-style hosting: extract it into your home directory, and &lt;code&gt;public_html/&lt;/code&gt; becomes the web root while the backend sits above it, out of public reach.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Keeping data safe without a framework
&lt;/h2&gt;

&lt;p&gt;Most workspaces store their state as JSON documents in a single &lt;code&gt;app_state&lt;/code&gt; table. That keeps the schema small, but it raises an obvious risk: two tabs (or a tab and an API client) editing the same document and silently overwriting each other.&lt;/p&gt;

&lt;p&gt;So every save carries a revision, the SHA-256 of the document the client last saw. The server compares it inside a transaction with the row locked; if it doesn't match, it returns &lt;code&gt;409 Conflict&lt;/code&gt; instead of writing. The browser keeps your unsaved edits and asks you to reload. It's plain optimistic concurrency, and it means no client can quietly overwrite newer data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Money that doesn't lie
&lt;/h2&gt;

&lt;p&gt;The Finance workspace taught me the most.&lt;/p&gt;

&lt;p&gt;Debts and credits are plans, not rows. A monthly rent payment, a loan split into twelve payments, or money a friend owes me once. &lt;strong&gt;Dues are derived when you look at a date range&lt;/strong&gt; rather than created by a cron job, and each due has a stable ID (&lt;code&gt;plan-id:index&lt;/code&gt;), so refreshing can never duplicate one.&lt;/p&gt;

&lt;p&gt;A few rules turned out to matter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Terms are fixed after creation.&lt;/strong&gt; If you could edit a plan's amount, every past due would quietly change meaning. You can rename a plan, stop a recurring one from a date, or delete one entered by mistake, but only while nothing on it has been paid.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Month-end dates keep their anchor.&lt;/strong&gt; A plan that starts on 31 January is due on 28 or 29 February and back on 31 March, not drifting to the 28th forever.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Paying is atomic.&lt;/strong&gt; Recording a payment writes the decision and the ledger entry (an expense for a debt, an income for a credit) in one MySQL transaction, with revision checks on every document involved. Undo removes the entry only if you haven't edited it since.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Splits are exact.&lt;/strong&gt; A total divided into N payments uses whole Toman amounts, with the rounding remainder in the last payment.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Letting an AI assistant in, carefully
&lt;/h2&gt;

&lt;p&gt;This is the part I'm most excited about. Edi Life OS has a small HTTP API protected by a dedicated bearer token, and a stdio &lt;strong&gt;MCP server&lt;/strong&gt; with 26 tools on top of it: dashboard, goals, tasks, habits, finance and notes.&lt;/p&gt;

&lt;p&gt;That means I can ask Claude things like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Show me my dashboard."&lt;/p&gt;

&lt;p&gt;"Create a SMART goal called Build a home studio, due in three months, and add Buy acoustic panels as a task."&lt;/p&gt;

&lt;p&gt;"Mark Reading complete for today."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;…and it reads or updates my real data.&lt;/p&gt;

&lt;p&gt;The design rules I followed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The MCP server only talks to the API.&lt;/strong&gt; It has no database credentials, no SQL tool and no way to touch arbitrary state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Destructive tools are marked as destructive&lt;/strong&gt;, so the client asks before deleting anything.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secret notes stay hidden&lt;/strong&gt; unless the server explicitly allows them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Writes are idempotent where possible&lt;/strong&gt; (completing a habit twice is harmless), and the browser's revision checks mean an AI edit can never be silently overwritten by a stale tab, or the other way round.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Small things I care about
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reviews you can share without oversharing.&lt;/strong&gt; After a weekly review, you can export a 1080×1350 image of your six-area scores. It contains numbers only; your reflections never leave the app.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tehran time everywhere&lt;/strong&gt;, because "today" should mean my today.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Light and dark themes&lt;/strong&gt;, and layouts that work on a phone.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How it was built
&lt;/h2&gt;

&lt;p&gt;I built Edi Life OS with heavy help from Claude Code: designing, writing and reviewing code, and testing flows in a real browser. I made the decisions; the AI did a lot of the typing and caught a fair number of bugs. Building software this way is a big part of why a one-person project could reach this scope, and the code is open for anyone to review.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it, and tell me what's missing
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Repo: &lt;a href="https://github.com/edrisranjbar/lifeos" rel="noopener noreferrer"&gt;github.com/edrisranjbar/lifeos&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Release: &lt;a href="https://github.com/edrisranjbar/lifeos/releases/tag/v1.0.0" rel="noopener noreferrer"&gt;v1.0.0&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;License: MIT&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's designed for a single owner, so it's a personal tool rather than a SaaS. I'd love feedback, especially on the MCP design and the debts-and-credits model. What would you want an AI assistant to handle in your daily planning?&lt;/p&gt;

</description>
      <category>selfhosted</category>
      <category>mcp</category>
      <category>php</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
