A self-hosted, zero-knowledge, secure PHP alternative to Obsidian.
Your notes, in your browser, on your server β every byte encrypted with AES-256 using a key only you know. No database, no cloud, no build step, no tracking.
What is Notyx?
Notyx is a single-folder PHP application that gives you an Obsidian-style
Markdown notebook you fully own and host yourself. It looks and feels like
Obsidian β a file tree, folders, a live-preview Markdown editor β but every note
is stored on disk as an encrypted .md.enc file, and the encryption key is
derived directly from your password and never stored anywhere.
If someone copies your data/ folder, they get nothing but ciphertext. Lose the
password and even you can't recover the notes β that's the point.
TL;DR: Obsidian's writing experience + a zero-knowledge encrypted vault, running on plain PHP you can drop on any cheap shared host.
β¨ Features
Writing
- Obsidian-style Live Preview editor built on CodeMirror 6 β Markdown
syntax characters (
#,**,`,>, linksβ¦) are hidden and rendered inline, and revealed again only on the line your cursor is on. - GitHub-Flavored Markdown: tables, task lists, strikethrough.
- Read mode with full Markdown rendering (via
marked). - Graceful fallback β if the CDN that serves CodeMirror is unreachable, the editor degrades to a plain Markdown textarea and keeps working offline.
- Autosave with a configurable debounce interval, plus
Ctrl/Cmd + Sto save on demand.
Organization
- Nested folders and a collapsible file tree, just like a vault.
- Note name = file name (Obsidian convention).
- Full-text search across note titles.
- Drag-and-drop import of existing
.md/.markdown/.txtfiles (bulk import never overwrites β duplicates get a numeric suffix). - Right-click context menu: new note, new subfolder, rename, delete.
Security & privacy
- Zero-knowledge AES-256 encryption. Notes are encrypted with
AES-256-CBCusing Encrypt-then-MAC (HMAC-SHA256) for tamper detection. - Your password is the key. It is run through PBKDF2-SHA256 (200,000 iterations) to derive the encryption + MAC keys. No password hash, no verification blob, and no per-file salt is ever written to disk.
- Nothing to steal at rest. The
data/folder holds only opaque ciphertext. - Change password re-encrypts every note with the new key in a safe two-pass operation (decrypt all β write all).
- Hardened sessions: the derived key lives only in the PHP session for the
session's lifetime (8 h by default);
HttpOnly,SameSite=Strict, strict-mode cookies. - Defense in depth:
.htaccessdenies direct web access todata/, path-traversal is blocked server-side, and responses sendX-Content-Type-Options: nosniff.
Self-hosting
- No database. No Composer. No Node build. Just PHP files.
- 5 built-in languages: English, Π£ΠΊΡΠ°ΡΠ½ΡΡΠΊΠ°, PortuguΓͺs, EspaΓ±ol, Deutsch.
- Drops into any PHP 8.1+ web root and runs.
π Installation
Requirements
- PHP 8.1+ with the OpenSSL extension enabled (bundled in most builds).
- A web server (Apache with
.htaccesssupport, or nginx with an equivalentdenyrule fordata/). - A writable
data/directory. - (Optional) Outbound internet access on the client for the CodeMirror / marked CDN β without it the app falls back to the plain-textarea editor.
Steps
# 1. Clone into your web root
git clone https://github.com/<you>/notyx.git
cd notyx
# 2. Make sure PHP can write the data directory
chmod 700 data # the app also creates it automatically
# 3. Point your web server at this folder, then open it in a browser
On first visit you'll be asked to create a vault password. That password becomes your encryption key β choose a strong one and don't lose it.
Local quick start (PHP built-in server)
php -S localhost:8000
# open http://localhost:8000
β οΈ The built-in server ignores
.htaccess. Use it only for local testing; for anything exposed to a network, serve behind Apache/nginx sodata/is protected.
βοΈ Configuration
All settings live in config.php:
| Constant | Default | Description |
|---|---|---|
DATA_DIR |
./data |
Where encrypted notes are stored. |
CIPHER |
aes-256-cbc |
OpenSSL cipher. |
PBKDF2_ITERATIONS |
200000 |
Key-derivation cost. |
KDF_SALT |
Notyx::static-kdf-salt::v1 |
In-code KDF salt β change this to a unique random string before creating your first note (see Security notes). |
AUTOSAVE_SEC |
0.8 |
Autosave debounce (seconds). Editable from the in-app Settings. |
SESSION_LIFETIME |
28800 (8 h) |
Session validity window. |
The autosave interval can also be changed at runtime from β Settings, which
rewrites the value in config.php.
π Security model
Notyx is zero-knowledge by design:
- You type a password. It is stretched with PBKDF2-SHA256 (200k iterations) into 64 bytes of key material β 32 bytes for AES-256, 32 bytes for HMAC.
- Each note is stored as
[IV (16B)][HMAC-SHA256 (32B)][ciphertext]. - On unlock, Notyx verifies the password by trial-decrypting the first note and checking the MAC. An empty vault accepts any password (it becomes the key for the notes you create).
- The key exists only in the server-side session while you're logged in. The password and key are never written to disk.
Notes & caveats (please read)
- The
KDF_SALTis a constant inconfig.php, not a secret stored per install. For best results, replace it with your own random value before creating any notes. Once notes exist, changing it would make them unreadable (use Change password to re-key instead). - Lose the password = lose the data. There is no recovery, by design.
- No login rate-limiting is built in β use a strong password and/or put the app behind your server's auth / fail2ban if it's internet-facing.
- The Markdown read view renders raw HTML in your notes. Since you author your own notes this is normally fine, but don't paste untrusted HTML and then open it in Read mode.
- For anything public, serve over HTTPS so the password isn't sent in the clear.
ποΈ Project structure
notyx/
βββ index.php # App shell: login screen + editor UI
βββ api.php # JSON API (status, login, CRUD, change password, settings)
βββ config.php # All configuration constants
βββ lib/
β βββ Auth.php # Passwordβkey login, session, change-password re-encrypt
β βββ Crypto.php # AES-256-CBC + Encrypt-then-MAC (HMAC-SHA256)
β βββ Notes.php # Encrypted note/folder storage on the filesystem
β βββ Settings.php # Persists autosave interval back into config.php
β βββ I18n.php # Tiny translation helper
βββ lang/ # en Β· uk Β· pt Β· es Β· de
βββ assets/
β βββ app.js # Client logic (tree, tabs, autosave, API calls)
β βββ editor.js # CodeMirror 6 Live Preview editor
β βββ style.css # Obsidian-like dark theme
β βββ favicon.svg # Brand mark
βββ data/ # Encrypted *.md.enc notes (web-access denied)
π οΈ Tech stack
- Backend: plain PHP 8.1+ (no framework, no dependencies), OpenSSL.
- Editor: CodeMirror 6 (loaded as ESM from a CDN) with a custom
Live-Preview decoration layer;
markedfor read mode. - Storage: the filesystem β one encrypted file per note.
- No database, no build pipeline.
π License
MIT license
Comments