hyprcrd
A self-hosted card/note manager with markdown, tags, image galleries, and Obsidian-compatible export.
Requirements
- PHP 8.3+
- Apache with
mod_rewrite - PHP extensions:
pdo_sqlite,zip
No external dependencies. All assets are bundled locally.
Quick Start (Docker)
git clone https://git.uesome.de/tanztee/hyprcrd.git
cd hyprcrd
docker build -t hyprcrd .
docker run -d -p 8080:80 -v hyprcrd-data:/var/www/html/data hyprcrd
Open http://localhost:8080 and set your password on first visit.
The -v hyprcrd-data:/var/www/html/data flag persists the database across container restarts.
Manual Installation
1. Clone
git clone https://git.uesome.de/tanztee/hyprcrd.git
cd hyprcrd
2. Set document root
Point your web server's document root to the public/ directory.
Apache vhost example:
<VirtualHost *:80>
ServerName hyprcrd.local
DocumentRoot /path/to/hyprcrd/public
<Directory /path/to/hyprcrd/public>
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
Make sure mod_rewrite is enabled:
a2enmod rewrite
systemctl restart apache2
3. Set permissions
mkdir -p data public/uploads
chmod 755 data
chmod 777 public/uploads
The web server user (e.g. www-data) needs write access to both directories.
4. PHP settings (optional)
For larger image uploads, adjust in php.ini:
upload_max_filesize = 20M
post_max_size = 22M
5. Open and set password
Navigate to your URL. On first visit, you'll be prompted to set a password (min. 4 characters). That's it.
Directory Structure
hyprcrd/
├── config.php # App constants (auto-configured)
├── Dockerfile
├── docker-entrypoint.sh
├── dist.sh # Build distribution ZIP
├── src/
│ ├── bootstrap.php # Initialization
│ ├── Database.php # SQLite schema & migrations
│ ├── CalDavClient.php # CalDAV HTTP client + iCal parser
│ └── CalDavSync.php # Bidirectional CalDAV sync engine
├── public/ # Document root
│ ├── index.php # Entry point & API
│ ├── .htaccess # URL rewriting
│ ├── css/
│ │ ├── style.css # App styles
│ │ ├── tailwind.out.css # Generated Tailwind utilities
│ │ ├── fonts.css # Local @font-face rules
│ │ └── vendor/ # Phosphor Icons CSS + fonts
│ ├── fonts/ # JetBrains Mono & Silkscreen
│ ├── js/
│ │ ├── app.js # Orchestrator
│ │ ├── modules/ # Feature modules
│ │ └── vendor/ # marked.js, DOMPurify
│ └── uploads/ # User media (writable)
├── relay/ # Relay service (mobile inbox + card sharing)
│ ├── index.php # Single-file API + Mobile UI
│ ├── .htaccess # URL rewriting + data protection
│ ├── Dockerfile
│ └── dist.sh # Build relay distribution ZIP
└── data/
└── hyprcrd.db # SQLite database (auto-created)
Features
- Cards with markdown body, color, tags, and position
- Buckets for organizing cards into groups
- Tags written as
#tagnameor hierarchical#parent/childin card body, extracted automatically - Markdown with
[[Card Title]]wiki-links,```galleryimage galleries,```treelist views - Search combining text and
#tagfilters (e.g.#project meeting notes) - Image gallery with orphan detection and fullscreen viewer
- Split view showing tag-related and content-related cards
- Import/Export as JSON+media ZIP or Obsidian/Logseq-compatible markdown vault
- Drag & drop card reordering and grouping
- Calendar with month view, journal entries, appointments, and day popup
- CalDAV sync with ownCloud/Nextcloud (bidirectional,
#termintagged cards) - Inline calculator (
?3+5→8,34+56?→34+56=90) - Snippets (
?dat,?time,?now,?todo,?table,?code, ...) - Due dates with overdue notifications and pulsing cards
- Relay for mobile note capture and card sharing between instances
- Notification banner for upcoming events, birthdays, overdue tasks
- Board split view with side-by-side preview
- Superview compact multi-column list with hover cards
- Keyboard shortcuts:
Ctrl+Ksearch,F1help,Shift+scrollzoom, arrow keys in autocomplete
Data & Backup
All data lives in two places:
data/hyprcrd.db— SQLite database (cards, buckets, settings)public/uploads/— uploaded images and audio
Back up both to preserve everything. The database auto-migrates on startup.
Export Formats
- JSON export (
exportbutton) — full backup with media, re-importable - Markdown vault (
vaultbutton) — Obsidian/Logseq-compatible structure:Bucket Name/ Card Title.md # YAML frontmatter + markdown body attachments/ image.jpg
CalDAV Sync (ownCloud / Nextcloud)
hyprcrd can sync appointment cards with any CalDAV server (tested with ownCloud 10.15.3.0).
Setup
- Open the calendar (button in header)
- Click the gear icon in the calendar header
- Enter your CalDAV URL, username, and app password:
URL: https://cloud.example.com/remote.php/dav/calendars/your-user/personal/ Username: your-user Password: your-app-password - Click test to verify the connection
- Click save
How it works
- Cards tagged with
#terminare synced as calendar events - The appointment card template maps to iCalendar fields:
Title: 2026-04-15 — Team Meeting **Zeit:** 14:30-15:30 **Ort:** Room 42 **Teilnehmer:** Alice, Bob --- **Notizen:** Agenda items here #termin - Sync is bidirectional: local changes push to the server, remote changes pull to hyprcrd
- Conflict resolution: last-modified timestamp wins
- Deleted cards: tracked via tombstones, deleted on the server on next sync
- Auto-sync: triggers when opening the calendar (max once per minute)
- Manual sync: click the cloud icon in the calendar header
Security
- Credentials are encrypted at rest (AES-256-CBC) in the SQLite database
- All sync operations run server-side (PHP/cURL), no client-side credential exposure
- Supports HTTPS with certificate verification
Calendar
The built-in calendar shows cards on their dates:
- Journal cards (title =
2026-04-08) appear on that date - Appointment cards (title =
2026-04-08 — Subject) appear on their date - Other cards fall back to their creation date
- Click a day to see all cards in a popup
- Double-click or use + journal / + termin to create new entries
- Appointment template includes Zeit, Ort, Teilnehmer, Notizen fields
- Navigate months with arrow buttons, today jumps to current date
Vault Key (Multi-Instance Encryption)
When running multiple hyprcrd instances that share CalDAV credentials, you need a shared encryption key.
Setup
- Go to Settings > Account > Vault Key
- Enter the same key (min 8 characters) on all instances
- Click save
How it works
- The vault key encrypts/decrypts CalDAV credentials stored in the database
- Key derivation:
SHA-256(vault_key)→ deterministic, same on all instances - Without a vault key, encryption falls back to the login password hash (single-instance only)
- Changing the vault key automatically re-encrypts CalDAV credentials
- Changing the login password does NOT affect encryption when a vault key is set
Important
- The vault key is stored in plaintext in each instance's database — it is NOT synced
- You must enter it manually on each connected instance
Relay
A minimal web service for mobile note capture and card sharing between hyprcrd instances.
Architecture
[Phone/Browser] → https://relay.example.com → [Relay Service]
↓ poll
[hyprcrd (local)]
[hyprcrd A] → push card → [Relay] ← poll ← [hyprcrd B]
Deploy to Webspace
cd relay
./dist.sh
Upload the resulting hyprcrd-relay.zip to your webspace and extract it. The SQLite database is created automatically on first request. Open the URL and set a password + shared secret.
Requirements: PHP 8.3+ with pdo_sqlite, Apache with mod_rewrite.
Deploy with Docker
cd relay
docker build -t hyprcrd-relay .
docker run -d -p 8081:80 -v relay-data:/var/www/html/data hyprcrd-relay
Development (both together)
./dev.sh
Starts hyprcrd on http://localhost:8088 and the relay on http://localhost:8081 in the same Docker network. Source files are bind-mounted — changes are live without rebuilding.
From inside the hyprcrd container, the relay is reachable at http://relay:80.
Setup
- Open the relay URL, set a password (for UI login) and a shared secret (for encryption)
- In hyprcrd: Settings > Relay — enter the relay URL and the same shared secret
- Click save
How it works
- Mobile inbox: type a note on the relay UI, it arrives as a card with
#inboxtag - Push to relay: right-click any card → "push to relay" — other instances pick it up on next poll
- Messages are encrypted client-side (AES-256-CBC) before sending to the relay
- The relay only stores ciphertext — it cannot read your messages
- hyprcrd polls the relay every 45 seconds in the background
- Messages are kept on the relay for 24 hours, then auto-deleted
- Cards pushed from the same instance are not re-imported
- Enter to send, Shift+Enter for newline
Relay Security
- Separate password (UI login) and shared secret (API + encryption)
- Bcrypt hashing for both credentials
- Rate-limited login (5 attempts/minute)
- AES-256-CBC client-side encryption — relay never sees plaintext
- Security headers (X-Frame-Options, HSTS, X-Content-Type-Options)
- SQLite database blocked from direct download via
.htaccess - Request size limited to 256KB
Rebuilding Tailwind CSS
After changing Tailwind utility classes in HTML or JS files:
# Download standalone CLI (once)
curl -sL https://github.com/tailwindlabs/tailwindcss/releases/latest/download/tailwindcss-linux-x64 -o tailwindcss && chmod +x tailwindcss
# Build
./tailwindcss --input public/css/tailwind.css --output public/css/tailwind.out.css --content "public/index.php,public/js/**/*.js" --minify
Security
- Bcrypt password hashing
- CSRF protection on all mutations
- Rate-limited login (5 attempts/60s)
- Upload directory blocks PHP execution
- DOMPurify sanitizes all rendered markdown
- Security headers (X-Frame-Options, HSTS, etc.)
- AES-256-CBC encryption for CalDAV credentials and Relay messages
- Vault key system for multi-instance CalDAV encryption (independent of login password)
- Relay: separate password + shared secret, rate-limited, client-side encryption
License
MIT