diff --git a/config.php b/config.php
index 8bb0179..f130f54 100644
--- a/config.php
+++ b/config.php
@@ -1,7 +1,7 @@
Last deploy: 2026-04-08 by @bob
+> Rollback: \`git revert HEAD && ./deploy.sh\`
+
+#ops #deploy`,
+ },
+ {
+ title: 'Meeting 2026-04-15 — Retro',
+ color: COLORS.yellow,
+ due_date: '2026-04-15',
+ body: `**Zeit:** 14:00-15:00
+**Ort:** Room 42
+**Teilnehmer:** Alice, Bob, Carol
+
+---
+
+### What went well
+- Deploy frequency improved (3x/week → daily)
+- Zero P1 incidents this sprint
+- New search feature got good feedback
+
+### What to improve
+- PR review turnaround still >24h
+- Flaky integration tests causing false alarms
+- Documentation gaps in relay module
+
+### Action items
+- [ ] @alice: set up PR review SLA
+- [ ] @bob: fix top 5 flaky tests
+- [ ] @carol: write relay onboarding doc
+
+#meeting #termin #retro`,
+ },
+ {
+ title: 'Reading list',
+ color: COLORS.slate,
+ body: `Papers and articles to read:
+
+- [Designing Data-Intensive Applications](https://dataintensive.net/) — Ch. 7-9
+- [The Twelve-Factor App](https://12factor.net/)
+- \`CAP theorem revisited\` — Brewer 2012
+- Local-first software — Kleppmann et al.
+
+### Notes
+> "The truth is the log. The database is a cache."
+> — Pat Helland
+
+#personal #research`,
+ },
+ {
+ title: 'API v3 migration',
+ color: COLORS.blue,
+ body: `### Endpoints to migrate
+
+| v2 | v3 | Status |
+|----|-----|--------|
+| GET /users | GET /api/v3/users | done |
+| POST /auth | POST /api/v3/auth/token | done |
+| GET /cards | GET /api/v3/cards | in progress |
+| WS /stream | WS /api/v3/events | planned |
+
+### Breaking changes
+- Auth header: \`Bearer\` instead of \`X-Token\`
+- Pagination: cursor-based instead of offset
+- Error format: \`{"error": {"code": ..., "message": ...}}\`
+
+[[Architecture RFC]]
+
+#project/api #migration`,
+ },
+ {
+ title: 'Infra cost review',
+ color: COLORS.orange,
+ body: `### Monthly breakdown (April)
+
+\`\`\`
+Compute (k8s) €340 ████████░░ 68%
+Storage (S3) €85 ██░░░░░░░░ 17%
+Database (RDS) €52 █░░░░░░░░░ 10%
+CDN + DNS €18 ░░░░░░░░░░ 4%
+Monitoring €8 ░░░░░░░░░░ 2%
+─────────────────────────────────────
+Total €503
+\`\`\`
+
+Budget: €600 → **€97 headroom**
+
+Next action: evaluate spot instances for batch workers.
+
+#ops #infra #finance`,
+ },
+ {
+ title: 'Birthday Alice',
+ color: COLORS.pink,
+ due_date: '2026-06-12',
+ body: `#geburtstag`,
+ },
+ {
+ title: 'Onboarding: new developer',
+ color: COLORS.green,
+ body: `## First week checklist
+
+- [ ] Laptop + accounts provisioned
+- [ ] SSH key added to Gitea
+- [ ] Access to staging cluster
+- [x] Slack channels joined
+- [x] 1:1 with team lead
+- [ ] Read [[Architecture RFC]]
+- [ ] First PR: fix a good-first-issue
+
+### Contacts
+| Role | Person |
+|------|--------|
+| Buddy | @carol |
+| Team lead | @alice |
+| Infra | @dave |
+
+#onboarding #ops`,
+ },
+ {
+ title: 'Competitive analysis',
+ color: COLORS.purple,
+ body: `### Landscape
+
+| Product | Self-hosted | Offline | E2E encryption |
+|---------|:-----------:|:-------:|:--------------:|
+| Notion | no | no | no |
+| Obsidian| yes | yes | paid |
+| Logseq | yes | yes | no |
+| **hyprcrd** | **yes** | **yes** | **yes** |
+
+### Differentiators
+- Tag extraction from body (zero-friction categorization)
+- Relay model: multi-tenant sync without cloud dependency
+- Plugin system: extensible without forking
+- CalDAV native (not just ical export)
+
+#research #strategy`,
+ },
+ {
+ title: 'Security audit notes',
+ color: COLORS.red,
+ body: `## Audit 2026-Q1 findings
+
+### Critical
+~~1. Session token in URL parameter~~ → fixed in v1.2.1
+
+### High
+2. CSRF token not rotated on privilege change → [[Auth timeout on mobile]]
+
+### Medium
+3. Upload directory allows .svg (XSS vector)
+4. Rate limiter not applied to /api/export
+
+### Low
+5. Server version exposed in HTTP headers
+6. No CSP header on main app
+
+> Next audit scheduled: 2026-07-15
+
+#security #audit #urgent`,
+ },
+ {
+ title: 'Design system tokens',
+ color: COLORS.purple,
+ body: `### Color palette
+
+\`\`\`
+--t-bg: #0f172a base background
+--t-surface: #1e293b card/panel surface
+--t-border: #334155 borders
+--t-text: #94a3b8 body text
+--t-bright: #e2e8f0 headlines
+--t-accent: #38bdf8 primary accent
+--t-accent2: #a78bfa secondary accent
+--t-danger: #fb7185 errors/destructive
+\`\`\`
+
+### Typography
+- **UI:** Silkscreen (pixel font)
+- **Content:** JetBrains Mono
+- **Sizes:** 9px labels, 11px body, 13px headings
+
+### Radius scale
+\`--ui-radius-card\`, \`--ui-radius-modal\`, \`--ui-radius-input\`
+
+All overridable per theme preset.
+
+#design #frontend`,
+ },
+ {
+ title: 'Customer feedback digest',
+ color: COLORS.yellow,
+ body: `### Week of 2026-04-07
+
+**Positive:**
+- "Search is blazing fast" — 3 mentions
+- "CalDAV sync just works" — 2 mentions
+- "Love the keyboard shortcuts" — @user42
+
+**Requests:**
+- Mobile app (→ Electron PWA?)
+- Shared boards without relay (→ collab-buckets?)
+- Dark mode for relay UI (→ already shipped!)
+
+**Issues:**
+- Image upload fails >5MB on shared hosting
+- Calendar popup overlaps on 13" screens
+
+#feedback #product`,
+ },
+ {
+ title: 'Database schema notes',
+ color: COLORS.slate,
+ body: `### Core tables
+
+\`\`\`sql
+cards (id, title, body, color, tags,
+ bucket_id, parent_id, position,
+ due_date, relay_uid,
+ created_at, updated_at)
+
+buckets (id, name, color, parent_id,
+ collab_uid, position)
+
+settings (key, value)
+\`\`\`
+
+### Indexes
+- \`cards.bucket_id\` — bucket filter
+- \`cards.parent_id\` — group lookup
+- \`buckets.collab_uid UNIQUE\` — relay sync
+
+SQLite WAL mode for concurrent reads.
+
+#architecture #database`,
+ },
+ {
+ title: 'Release notes v1.3',
+ color: COLORS.green,
+ body: `## What's new
+
+### Features
+- **Tooltip system** — custom styled tooltips replace all native titles
+- **Asset picker** — thumbnail grid in the editor toolbar
+- **Super-bucket split** — click activates, caret opens dropdown
+- **Watch plugin** — analog clock overlay with SVG watchface
+- **Card peek** — hover preview with rendered markdown
+
+### Fixes
+- Shortcuts no longer fire when typing in inputs
+- Gallery opens even if API fetch fails
+- Relay poll suppressed during user actions
+- Find/replace integrated into search overlay
+
+### Breaking
+- All \`title=""\` attributes migrated to \`data-tip=""\`
+
+#release #changelog`,
+ },
+ {
+ title: '2026-04-12',
+ color: COLORS.slate,
+ body: `## Journal
+
+Started the day reviewing PRs. Merged the tooltip system and the asset picker.
+
+Spent the afternoon on the Electron build — static PHP binary works but the WAL/SHM files caused issues. Fixed by deleting stale lock files.
+
+Had a good session on the watch plugin. The wobble animation on the second hand is a nice touch.
+
+**Tomorrow:**
+- [ ] Test relay collab-buckets with two instances
+- [ ] Write release notes
+- [ ] Screenshot for website
+
+#journal`,
+ },
+ {
+ title: 'Backup strategy',
+ color: COLORS.orange,
+ body: `### Current
+
+Daily cron:
+\`\`\`bash
+#!/bin/bash
+DATE=$(date +%Y-%m-%d)
+cp data/hyprcrd.db "/backup/hyprcrd-$DATE.db"
+tar czf "/backup/uploads-$DATE.tar.gz" public/uploads/
+find /backup -mtime +30 -delete
+\`\`\`
+
+### Planned
+- [ ] Automated export ZIP via API
+- [ ] Offsite copy to S3-compatible storage
+- [ ] Test restore monthly
+- [ ] Document in runbook
+
+> WAL checkpoint before copy:
+> \`sqlite3 data/hyprcrd.db "PRAGMA wal_checkpoint(TRUNCATE);"\`
+
+#ops #backup #infra`,
+ },
+ {
+ title: 'Plugin ideas',
+ color: COLORS.pink,
+ body: `### Brainstorm
+
+- **Kanban columns** — cards as swimlanes per bucket
+- **Time tracking** — start/stop timer per card, weekly report
+- **AI summary** — auto-generate card summaries with local LLM
+- **RSS reader** — fetch feeds into cards
+- **Webhook triggers** — POST on card create/update/delete
+- **PDF export** — render card as styled PDF
+- **Voice memo** — record audio, attach to card, transcribe
+
+### Criteria for inclusion
+1. Does it help *find* information?
+2. Can it be a drop-in plugin (no core changes)?
+3. Is it useful without an internet connection?
+
+#idea #plugins #brainstorm`,
+ },
+ {
+ title: 'Monitoring setup',
+ color: COLORS.green,
+ body: `### Stack
+
+- **Uptime:** UptimeRobot (free tier, 5min interval)
+- **Errors:** Sentry self-hosted → \`sentry.internal\`
+- **Metrics:** Grafana + Prometheus
+- **Logs:** Loki + promtail on each node
+
+### Alerts
+| Metric | Threshold | Channel |
+|--------|-----------|---------|
+| HTTP 5xx rate | >1% for 5min | Slack #ops-alerts |
+| Response time p95 | >2s for 10min | Slack #ops-alerts |
+| Disk usage | >85% | Email + Slack |
+| SSL expiry | <14 days | Email |
+
+Dashboard: \`grafana.internal/d/hyprcrd\`
+
+#ops #monitoring #infra`,
+ },
+];
+
+async function generate() {
+ const bucket = await api('api/buckets', { method: 'POST', body: { name: 'Project Alpha' } });
+ const bid = bucket?.id;
+ if (!bid) return false;
+ await api(`api/buckets/${bid}`, { method: 'PATCH', body: { color: COLORS.blue } });
+
+ for (const c of CARDS) {
+ await api('api/cards', {
+ method: 'POST',
+ body: { title: c.title, body: c.body, color: c.color, bucket_id: bid, due_date: c.due_date || null },
+ });
+ }
+ return true;
+}
+
+function init() {
+ const footer = document.getElementById('main-footer');
+ if (!footer) return;
+
+ const btn = document.createElement('button');
+ btn.className = 'flex items-center gap-1.5 px-2.5 py-1 text-xs border border-t-border text-t-muted hover:border-t-accent hover:text-t-accent transition-colors';
+ btn.innerHTML = 'Demo';
+ btn.dataset.tip = 'Create example bucket with demo cards';
+
+ btn.addEventListener('click', async () => {
+ btn.disabled = true;
+ btn.innerHTML = 'creating...';
+ const ok = await generate();
+ if (ok) {
+ btn.innerHTML = 'Done';
+ await window._reloadAll?.();
+ } else {
+ btn.innerHTML = 'Failed';
+ }
+ setTimeout(() => {
+ btn.innerHTML = 'Demo';
+ btn.disabled = false;
+ }, 2000);
+ });
+
+ const firstChild = footer.querySelector('div');
+ if (firstChild) firstChild.appendChild(btn);
+
+ h?.registerPlugin({ name: 'example-content', version: '1.0.0' });
+}
+
+if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', init);
+} else {
+ init();
+}
diff --git a/public/plugins/example-content/plugin.json b/public/plugins/example-content/plugin.json
new file mode 100644
index 0000000..afd8b56
--- /dev/null
+++ b/public/plugins/example-content/plugin.json
@@ -0,0 +1,8 @@
+{
+ "name": "example-content",
+ "version": "1.0.0",
+ "description": "One-click demo content for screenshots and first-time exploration.",
+ "frontend": {
+ "js": ["example-content.js"]
+ }
+}
diff --git a/website/index.html b/website/index.html
new file mode 100644
index 0000000..be54033
--- /dev/null
+++ b/website/index.html
@@ -0,0 +1,702 @@
+
+
+
+
+
+hyprcrd
+
+
+
+
+
+
+
+
+
+
+ hyprcrd
+
+
+
+
+
+
+
+
+
+
+
+
+ stop losing
+ information.
+
+
+ Cards. Tags. Wiki-links. Markdown. Calendar. Encrypted sync between devices.
+ One SQLite file. Zero cloud. Built for finding things, not filing them.
+
+
+ $git clone https://git.uesome.de/tanztee/hyprcrd
+ $cd hyprcrd && docker compose up -d
+ # open localhost:8088 — set password — done
+
+
+
+
+
+
+
+
SEARCH
+
+
+
Type to find. Instantly.
+
Full-text search hits titles, card bodies, and tags at the same time. Combine #tag filters with free text — #project deploy script finds exactly what you think it does.
+
+
Search + replace across every card in the database
+
#tag1 #tag2 for AND filtering
+
Scope to a specific bucket
+
Drag search results onto each other to group cards
+
Preview panel shows rendered markdown
+
+
+
+
>#project deploy
+
+ 3 results in 2ms
+
+ API redesign
+ ...the deploy script handles
+ staging and prod environments...
+
+ Infra checklist
+ ...deploy to k8s cluster
+ after running integration tests...
+
+ Sprint retro
+ ...discussed deploy
+ frequency and rollback strategy...
+
+
+
+
+
+
TAGS
+
+
+
Write a hashtag. It becomes structure.
+
No separate tag dialog. No filing step. Write #project in the card body and it extracts automatically into a clickable, filterable tag.
+
+
Hierarchical: #project/frontend/auth — each segment is independently clickable
+
Click to filter, Ctrl+click to combine (AND logic)
+
Tag quick-pick with T — searchable overlay of all existing tags
+
Assign via right-click context menu
+
Super-buckets: nest buckets into collapsible groups
Line operations: Alt+Up/Down to move, Shift+Alt to duplicate
+
Upload images, audio, PDFs — paste or drag
+
Asset picker: thumbnail grid with search
+
Extract selection to new card with auto-link
+
Find & replace within the card (Ctrl+H)
+
+
+
+
## Sprint planning Q3
+
+Goals for next quarter:
+
+- [x] Finish API redesign
+- [x] Migrate auth to [[Architecture RFC]]
+- [ ] Load testing
+- [ ] Deploy to prod
+
+**Deadline:** 2026-07-01
+**Owner:** @alice
+
+See also: [[Infra checklist]]
+
+#project/api#meeting
+
+
+
+
+
+
GITEA
+
+
+
Cards become tickets. Tickets stay linked.
+
Connect any number of Gitea instances. Browse issues, create tickets directly from cards, and keep your project management in sync without leaving hyprcrd.
+
+
Multi-account: connect unlimited Gitea servers with token auth
+
Default repo per account — pre-fills create form, stays editable
+
Right-click any card → Make ticket: title + body pre-fill the issue form
+
Full issue browser: filter by state, scope (assigned/created/mentioned)
+
+ Export: include plugin settings in backup
+
+ enhancement
+
+ #128
+
+
+
+
+
+
+
+
RELAY
+
+
+
Sync between devices. Share with people.
+
A self-hosted message relay that connects multiple hyprcrd instances. Per-user encryption. Collaborative buckets with exclusive card locking. Encrypted chat with read receipts.
+
+
Multi-tenant: each user gets their own login + encryption secret
+
Cards sync between devices — push from one, poll from another
+
Collaborative buckets: shared editing with exclusive locks
+
Chat: encrypted messages with delivery + read receipts
+
Compose + inbox UI works on mobile
+
24h ephemeral — the relay routes, it doesn't archive
+
AES-256-CBC at every hop, bcrypt passwords, master key at rest
+
+
+
+
+
+ alice
+ →
+ encrypts with own secret
+ →
+ relay
+
+
+
+ 1. authenticate sender
+ 2. decrypt with alice's secret
+ 3. re-encrypt with inter-tenant key
+ 4. store (24h TTL)
+ 5. on bob's poll: decrypt
+ 6. re-encrypt with bob's secret
+ 7. deliver
+
+
+
+ relay
+ →
+ re-encrypted for recipient
+ →
+ bob
+
+
+
+
+
+
+
+
EMAIL
+
+
+
Built-in mail client. Spam dies on arrival.
+
A focused IMAP mail client built into hyprcrd. Not a replacement for your full email workflow — a triage tool. See what's real, nuke what's not, move on.
+
+
Multi-account IMAP with encrypted credential storage
+ deals@sz-wholesale.cn
+ Limited offer!!! Best prices on electronics!!!
+ spam 87%
+
+
+ Sentry
+ New issue: TypeError in /api/relay/push
+ ? suspicious
+
+
+ crypto-gains@proton.me
+ Make 500% returns with this one trick
+ spam 98%
+
+
+
+
+
+
+
+
KEYBOARD
+
+
+
40+ shortcuts. Zero mouse required.
+
Every action has a key binding. Arrow keys navigate the board. Number keys switch buckets. Quick capture creates cards without leaving the keyboard.
+
+
Press ? for the full shortcut reference
+
Shortcuts auto-suppress in input fields
+
Context-aware: editor shortcuts only fire in the editor
+
Styled tooltips and inline help across the UI
+
+
+
+
+ ?
+ N
+ J
+ T
+ C
+ W
+ M
+ /
+ Esc
+ F1
+ Ctrl K
+ Ctrl N
+ Ctrl H
+ Ctrl S
+ Ctrl Z
+ ↑
+ ↓
+ ←
+ →
+ Enter
+ Tab
+ 1
+ 2
+ 3
+ ...
+ 9
+
+
+
+
+
+
+
DEPLOY
+
+
+
Docker. PHP host. Desktop app.
+
Three deployment paths, same app. The Electron build bundles a static PHP binary — no PHP installation needed. Data lives outside the app bundle and survives updates.
+
+
One SQLite file — back up by copying data/hyprcrd.db
+
Export as JSON+media ZIP or Obsidian/Logseq markdown vault
+
Plugin system: drop a folder, reload, feature appears