Lakron: A Minimal, Encrypted, Realtime Life Scheduler with Supabase
Lakron is a minimal, smart life scheduler that keeps your data private and in sync. Every task is encrypted before it leaves your browser, and realtime sync means your schedule is always up to date everywhere.
How Encryption Works
Every task you create is encrypted in the browser using a key derived from your password and a salt. Only you can decrypt your data—nobody else, not even the database admin.
// TaskProvider.tsx
const generateKey = useMemo(() => {
if (!currentProfile?.password) return ''
return CryptoJS.PBKDF2(currentProfile.password, ENCRYPTION_SALT, { keySize: 256 / 32 }).toString()
}, [currentProfile?.password])
When saving or loading a task, the title and description are encrypted/decrypted with this key:
// supabase.ts
function encrypt(text, key) {
return CryptoJS.AES.encrypt(text, key).toString()
}
function decrypt(cipher, key) {
return CryptoJS.AES.decrypt(cipher, key).toString(CryptoJS.enc.Utf8)
}
How to Clone and Run Lakron
- Clone the repo:
git clone https://github.com/laakri/lakron.git
cd lakron
- Install dependencies:
npm install
- Create a
.env
file in the root with your Supabase project keys. Use your own values, never share your real keys publicly!
VITE_ENCRYPTION_KEY=your-random-salt
VITE_SUPABASE_URL=https://your-project.supabase.co
VITE_SUPABASE_KEY=your-service-role-key
VITE_SUPABASE_ANON_KEY=your-anon-key
- Start the dev server:
npm run dev
Enable Realtime on the Tasks Table
Lakron uses Supabase Realtime to sync tasks instantly across devices.
To enable this, turn on Realtime for the tasks
table in your Supabase dashboard:
- Go to your Supabase project
- Click on "Database" → "Replication"
- Find the
tasks
table and enable Realtime
SQL to Create the Tables (All in One Block)
If you need to create the profiles
and tasks
tables from scratch, here’s the full SQL:
-- Create profiles table
CREATE TABLE IF NOT EXISTS profiles (
id SERIAL PRIMARY KEY,
name VARCHAR(100) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Create tasks table
CREATE TABLE IF NOT EXISTS tasks (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
title VARCHAR(255) NOT NULL,
time VARCHAR(10) NOT NULL,
date DATE NOT NULL,
completed BOOLEAN DEFAULT FALSE,
type VARCHAR(10) CHECK (type IN ('task', 'event')) DEFAULT 'task',
description TEXT,
profile_id INTEGER REFERENCES profiles(id) ON DELETE CASCADE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
priority INTEGER NOT NULL DEFAULT 2,
recurring BOOLEAN NOT NULL DEFAULT false,
recurrence_rule TEXT,
"completedDates" TEXT[]
);
-- Add constraint to ensure recurrence_rule values are valid
ALTER TABLE tasks
ADD CONSTRAINT IF NOT EXISTS check_recurrence_rule
CHECK (
recurrence_rule IN ('daily', 'weekly', 'monthly', 'yearly', 'custom')
OR recurrence_rule IS NULL
);
-- Optional: Standardize existing recurrence_rule values (safe to run even if table is empty)
UPDATE tasks SET recurrence_rule = 'daily' WHERE recurrence_rule = 'daily';
UPDATE tasks SET recurrence_rule = 'weekly' WHERE recurrence_rule = 'weekly';
UPDATE tasks SET recurrence_rule = 'monthly' WHERE recurrence_rule = 'monthly';
UPDATE tasks SET recurrence_rule = 'yearly' WHERE recurrence_rule = 'yearly';
UPDATE tasks SET recurrence_rule = 'custom'
WHERE recurrence_rule IS NOT NULL
AND recurrence_rule NOT IN ('daily', 'weekly', 'monthly', 'yearly');
Realtime Sync
Supabase’s realtime channels keep tasks in sync across tabs and devices. The provider subscribes to changes for the current profile, so if you add or complete a task on your phone, it updates instantly on your laptop.
// TaskProvider.tsx
const unsubscribe = dbOperations.subscribeToTasks(currentProfile.id, (payload) => {
// handle INSERT, UPDATE, DELETE
})
Deployment
Deploying Lakron is as smooth as using it. The whole process is automated with GitHub Actions, so every push to the main branch triggers a build and deploy.
The workflow does three things:
- Installs dependencies and builds the frontend
- Copies the built static files to the server using SCP over SSH
- Reloads the Caddy server so your changes go live instantly
Here’s the gist of the deployment workflow:
# .github/workflows/deploy.yml
- name: Install dependencies and build
run: |
npm ci
npm run build
- name: Deploy dist to server
uses: appleboy/[email protected]
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SERVER_SSH_KEY }}
source: "dist/*"
target: "/home/youruser/lakron/dist"
- name: Reload Caddy
uses: appleboy/[email protected]
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SERVER_SSH_KEY }}
script: |
sudo systemctl reload caddy
You just need to add your server’s SSH key and connection info as GitHub secrets.
Caddy serves the static files from the dist
directory, so as soon as the workflow finishes, your latest version is live.
No manual uploads, no downtime, just push and your encrypted, realtime scheduler is up to date for everyone.
Clone, set your env, run the SQL, enable realtime, and you’ve got a fully encrypted, realtime, minimal life scheduler.
Top comments (0)