No description
  • TypeScript 75.8%
  • SCSS 10.6%
  • Rust 5.7%
  • JavaScript 4.5%
  • Kotlin 1.1%
  • Other 2.1%
Find a file
Dennis cee28407bc
Some checks failed
/ release (push) Has been cancelled
(c) Release nobocloud 1.1.0
2026-11-03 15:25:16 +00:00
.forgejo/workflows (c) versioning scheme 2026-11-02 19:08:01 +01:00
.idea (c) updates 2026-08-04 22:38:19 +02:00
.vscode (+) Initial commit 2026-07-29 21:58:23 +02:00
containers (c) deployment 2026-10-26 20:22:23 +00:00
crates@08170041d7 (c) updated crates 2026-11-03 11:15:08 +01:00
docs (c) hooks updated, do not check ip, only token 2026-11-03 11:07:43 +01:00
flatpak (c) Add GPL-3.0-or-later license, REUSE.toml and SPDX headers 2026-10-24 20:14:10 +00:00
frontend-checks@259e5b8c41 (c) hooks updated, do not check ip, only token 2026-11-03 11:07:43 +01:00
graphics/logo (+) TaskFilter ctrl f toggleable, various fixes (JSX multiple reactive executions, leading to multiple instances) 2026-08-19 09:37:45 +02:00
LICENSES (c) Add GPL-3.0-or-later license, REUSE.toml and SPDX headers 2026-10-24 20:14:10 +00:00
messages (+) Add trash settings and orphaned attachment maintenance 2026-11-01 10:23:42 +00:00
pb (c) Release nobocloud 1.1.0 2026-11-03 15:25:16 +00:00
plugins (c) Pre-bundle all runtime dependencies in vite.config.ts to prevent dev reload cascade 2026-10-24 21:09:02 +00:00
project.inlang (+) Bookmarks: links with a note and tags, kept as org 2026-10-12 18:58:22 +00:00
public (+) Initial commit 2026-07-29 21:58:23 +02:00
scripts (c) versioning scheme 2026-11-02 19:08:01 +01:00
src (c) hooks updated, do not check ip, only token 2026-11-03 11:07:43 +01:00
src-tauri (f) Integrate hardened trash recovery and autosave 2026-11-01 19:28:28 +00:00
test (c) hooks updated, do not check ip, only token 2026-11-03 11:07:43 +01:00
tools (+) Concept for less login notifications 2026-11-02 16:39:17 +00:00
.env.template (c) Add GPL-3.0-or-later license, REUSE.toml and SPDX headers 2026-10-24 20:14:10 +00:00
.git-blame-ignore-revs (c) Add GPL-3.0-or-later license, REUSE.toml and SPDX headers 2026-10-24 20:14:10 +00:00
.gitignore (c) releasemanagement as in other apps 2026-11-02 18:59:24 +01:00
.gitmodules (c) Add GPL-3.0-or-later license, REUSE.toml and SPDX headers 2026-10-24 20:14:10 +00:00
.mise.toml (c) versioning scheme 2026-11-02 19:08:01 +01:00
CHANGELOG.md (c) hooks updated, do not check ip, only token 2026-11-03 11:07:43 +01:00
de.dencla.nobo.yml (c) Add GPL-3.0-or-later license, REUSE.toml and SPDX headers 2026-10-24 20:14:10 +00:00
flake.lock (c) updated flake 2026-11-03 16:24:50 +01:00
flake.nix (c) Fix python3 dbus module resolution in dev sandbox 2026-10-24 21:44:17 +00:00
frontend-checks.config.mjs (c) Add GPL-3.0-or-later license, REUSE.toml and SPDX headers 2026-10-24 20:14:10 +00:00
generated-cargo-sources.json (+) Flatpak build fixes 2026-10-20 23:04:44 +02:00
generated-pnpm-sources.json (+) Flatpak build fixes 2026-10-20 23:04:44 +02:00
index.html (c) Add GPL-3.0-or-later license, REUSE.toml and SPDX headers 2026-10-24 20:14:10 +00:00
LICENSE (c) Add GPL-3.0-or-later license, REUSE.toml and SPDX headers 2026-10-24 20:14:10 +00:00
package.json (c) releasemanagement as in other apps 2026-11-02 18:59:24 +01:00
pnpm-lock.yaml (c) pnpm stuff 2026-11-01 11:27:55 +01:00
pnpm-workspace.yaml (c) Add GPL-3.0-or-later license, REUSE.toml and SPDX headers 2026-10-24 20:14:10 +00:00
README.md (c) versioning scheme 2026-11-02 19:08:01 +01:00
renovate.json (+) deployment 2026-10-26 19:54:24 +00:00
REUSE.toml (c) releasemanagement as in other apps 2026-11-02 18:59:24 +01:00
rust-toolchain.toml (c) Add GPL-3.0-or-later license, REUSE.toml and SPDX headers 2026-10-24 20:14:10 +00:00
rustfmt.toml (c) Add GPL-3.0-or-later license, REUSE.toml and SPDX headers 2026-10-24 20:14:10 +00:00
tsconfig.json (f) Fixed inline markup character matching 2026-08-07 09:37:00 +02:00
tsconfig.node.json (+) Initial commit 2026-07-29 21:58:23 +02:00
vite.config.ts (c) Pre-bundle all runtime dependencies in vite.config.ts to prevent dev reload cascade 2026-10-24 21:09:02 +00:00
vitest.config.ts (c) Add GPL-3.0-or-later license, REUSE.toml and SPDX headers 2026-10-24 20:14:10 +00:00

nobo

Local-first Org-mode notes, tasks, projects, calendars, recipes and encrypted vault sync, built with Rust, Tauri and SolidJS.

Screenshots

Screenshots from the nobo Linux test sandbox with synthetic demo data and the local test server. Desktop, tablet and mobile layouts are shown below. Click a thumbnail to view the full-resolution image.

Agenda · Desktop Inbox · Desktop Projects · Desktop
Daily agenda Capture and organize inbox items Projects and next actions
Lists · Mobile Cookbook · Tablet Notes · Desktop
Synced shopping list on mobile Recipe ingredients and steps on tablet Org-mode note editor

Capture, organize and act: a nobo workflow

Watch the MP4 tour · All screenshots, dark themes and recording details

Clone

The shared Rust core lives in a submodule under crates/ and the frontend checks in frontend-checks/:

git clone --recurse-submodules https://github.com/denclade/nobo.git
git submodule update --init   # if already cloned without --recurse-submodules

Development environment (MicroVM)

Development runs inside an isolated NixOS MicroVM built from nix-dev-sandbox (flake.nix). Only the project directory is shared with the host.

1. Host setup (once)

sudo usermod -aG kvm $USER
mkdir -p ~/.config/nix
echo "experimental-features = nix-command flakes" >> ~/.config/nix/nix.conf
cp .env.template .env   # set DEV_SSH_PUBKEY and git identity

~/.ssh/config:

Host nobo-vm
    HostName 127.0.0.1
    Port 2200
    User dev
    StrictHostKeyChecking no
    UserKnownHostsFile /dev/null
    IdentityFile ~/.ssh/id_ed25519

2. Start and connect

nix run --impure
code --remote ssh-remote+nobo-vm /workspaces/nobo

Inside the VM, run mise run setup once to install the Rust toolchain, Android targets, submodules and pnpm dependencies.

3. Tasks (mise)

Run mise tasks to list all tasks:

Command Description
mise run setup One-time VM setup (Rust, Android targets, submodules, pnpm install).
mise run dev Starts Vite + Tauri dev server with the local test server and notification daemon (sandbox only, guarded by scripts/require-sandbox.sh).
mise run check Runs Rust tests in both workspaces and all frontend/architecture checks (pnpm check:all).
mise run check:lite Verifies every reduced feature combination.
mise run build:desktop Builds native Linux packages.
mise run install:system Installs the built binary, icons and .desktop file (PREFIX=/usr default, or PREFIX=$HOME/.local).
mise run uninstall:system Removes files installed by install:system.
mise run build:flatpak Generates offline sources and builds nobo.flatpak.
mise run flatpak:lint Validates flatpak/de.dencla.nobo.desktop and flatpak/de.dencla.nobo.metainfo.xml.
mise run dev:android Starts the KVM Android emulator and runs the app with hot reload.
mise run android:release Builds the signed Android release APK.
mise run release:prepare <version> Server release, step 1: sets pb/VERSION, commits, tags cloud-v<version> (no push; see Operations).
mise run release:deploy <tag> Server release, step 2: checks the tagged, clean state and builds deploy/ and dist/nobocloud-<tag>.tar.zst.
mise run build:deploy <name> Trial build of the server bundle without version checks (PR check); never a delivery.
mise run test:containers Builds every server role from deploy/ and starts it as restricted as on d0b (needs Podman).

For macOS / iOS (nix develop, mise run ios:init, mise run dev:ios, mise run build:ios), see docs/mobile.md.

For the D-Bus Secret Service / gnome-keyring setup in the VM, source scripts/dev-keyring.sh (done automatically by mise run dev; see docs/secrets.md).

Architecture

  • Rust core & feature plugins (crates/): crates/nobo-core/ holds Org parsing, indexing, search, history, settings and storage traits. Optional capabilities (nobo-babel, nobo-bookmarks, nobo-calendar, nobo-caldav, nobo-cookbook, nobo-review, nobo-sync) depend on the core, never the reverse (enforced by pnpm check:layers-rust). See docs/architecture.md and docs/core-api.org.
  • Tauri host (src-tauri/ & plugins/): Platform adapters (nobo-tauri) and native plugins (tauri-plugin-gestures, tauri-plugin-securestore, tauri-plugin-share).
  • Frontend (src/): SolidJS + CodeMirror 6 AST editor (orgKeymap.ts, orgLinePlugin.ts, orgInlinePlugin.ts, orgListPlugin.ts). Feature UI and command bindings live in src/features/<name>/ and register in src/features/index.ts. Coding and UI standards are documented in docs/qualitaet-und-coding-standards.md.
  • Releases: See docs/releasing.md and CHANGELOG.md.

Operations

The sync backend is a PocketBase instance, nobocloud, deployed on the server d0b (contract 1): rootless Podman pod under its own user, central Caddy in front, snapshot and automatic rollback on every deploy.

Service / user / pod nobocloud, number 10 (UID 10010, 1000:1000 in the container)
Domain nobocloud.dencla.de → 127.0.0.1:11090 → pod port 8090
Data /srv/nobocloud/pb_data → /pb/pb_data
Environment /etc/apps/nobocloud/pb.env (template: containers/env/pb.env.example)

What lives where:

  • containers/ – Containerfile, build.env (PocketBase version and checksum), Quadlets, site.caddy, smoke.txt. app deploy checks these files line by line; keep them free of header comments and within the rules of the contract.
  • pb/pb_migrations/ – creates sync_index and user_files and requires verified accounts (what docs/sync.md describes by hand). Runs on every start of a new release; existing collections are left alone. 1793030401_rate_limits.js turns on PocketBase's rate limiter (login, registration, mail-sending endpoints), trusts X-Forwarded-For from Caddy and sets the app URL used in mail links. 1761500000_d0b_mail_links.js points the links in PocketBase's mails at /konto.html instead of the dashboard, which d0b always answers with 403.
  • pb/pb_public/konto.html + konto.js – confirms an account from the link in the verification mail (the token sits after #, so it never reaches a log).
  • pb/pb_hooks/konto.pb.js – refuses password reset and email change: nobo derives the account password from vault password and email, so either would lock the account out for good without bringing the encrypted data back.

Release: the server has its own version, only in pb/VERSION (SemVer, pre-releases as 1.2.0-beta.2); its tag is cloud-v<version>. vX.Y.Z belongs to the app (docs/releasing.md).

mise run release:prepare 1.0.0            # commit "(c) Release nobocloud 1.0.0" + annotated tag cloud-v1.0.0
mise run release:deploy cloud-v1.0.0      # checks, tests, deploy/ and dist/nobocloud-cloud-v1.0.0.tar.zst (+ .sha256)
mise run test:containers                  # from deploy/: rootless, read-only, no capabilities, health, UID 1000
git push origin main && git push origin cloud-v1.0.0   # only now; .forgejo/workflows/release.yml attaches the bundle

release:prepare (scripts/cloud-release-version.mjs) refuses an invalid version, one not greater than the current, a dirty tree and an existing tag. release:deploy (scripts/cloud-release-deploy.mjs) sets no version: it requires the tag to match pb/VERSION, a clean tree and HEAD on the tag, so the commit in MANIFEST is exactly what was tagged. Rebuilding without a new version means the same tag again. The bundle (scripts/cloud-deploy-bundle.mjs) is an allowlist (containers/, pb/pb_migrations, pb/pb_hooks, pb/pb_public) and rejects links and special files. Nothing is pushed or uploaded by these commands. Then, as root on d0b: app deploy nobocloud cloud-v1.0.0. First-time setup:

app add nobocloud 10
editor /etc/apps/nobocloud/app.conf     # DOMAIN=nobocloud.dencla.de  REPO=https://zumcode.de/<owner>/nobo
install -m 0640 -o root -g nobocloud /dev/null /etc/apps/nobocloud/pb.env
app deploy nobocloud <tag>

Releases must be downloadable without login; otherwise fetch the bundle first and run app deploy nobocloud <folder>.

After the first deploy, open the dashboard through a tunnel (ssh -p 1505 -L 8090:127.0.0.1:11090 d0b, then http://localhost:8090/_/), create the superuser and set Settings → Mail settings: SMTP mail.d0b.de:465 (TLS), account sys-nobocloud. Check that Application URL reads https://nobocloud.dencla.de (set by the migration), send a test mail, then register once in nobo from start to finish. Without mail, nobody can verify an account and therefore nobody can sync.

Moving from an old server: copy pb_data to /srv/nobocloud/pb_data, then chown -R nobocloud: /srv/nobocloud. Secrets go into the .env files on the server, never into the repository.

Updates: d0b rebuilds the images nightly with base-image patches. Renovate (renovate.json) proposes PocketBase updates, recomputes PB_SHA256 via scripts/pb-sha256.sh (the Renovate instance must allow that command), and merges patches only once .forgejo/workflows/server.yml is green.

License

Copyright 2026 Dennis Burgermeister. Licensed under GPL-3.0-or-later. Source files carry SPDX headers; REUSE.toml covers generated files, fixtures and assets (reuse lint).