Skip to content

STAIxBWLB/maru

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

329 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Maru

Local-first AI workspace desktop app for Korean knowledge/document operations. Tauri 2 + Rust + React 19 + TypeScript. Current version 0.4.0.

Maru is the author SSOT for a single user's ~/workspace/work/ β€” it edits markdown with byte-identical frontmatter, ingests an inbox, runs bundled Claude Code skills, drives Korean document (HWPX/DOCX/PDF) operations, and visualizes the vault as a knowledge graph. Releases before v0.3.0 shipped under the name Anchor; the M0 rename (kr.maru.desktop, ~/.maru/) landed in v0.3.0.

Status (2026-07-11)

Phase State Outcome
0 β€” Hardening βœ… shipped Open existing workspaces safely. Frontmatter byte-identical round-trip. Multi-workspace registry. ko/en parity.
0.5 β€” UI polish βœ… shipped Topbar, sidebar with type filters + recents, command palette (⌘K), Pretendard Korean typography, light/dark.
1A β€” Killer feature MVP βœ… shipped Doc-selection reliability, frontmatter inline edit (InspectorPane), wikilink autocomplete (Korean IME-aware) + click-to-navigate, typed neighborhood pane, in-memory nav history (⌘[ / ⌘]).
1B β€” Rich editor / git βœ… feature-complete Git status badge + commit-from-app (file list + per-file diff + syntax color). Rayon-parallel workspace scan + cache-backed warm startup. Multi-tab editor (⌘1..⌘8 / ⌘W, dirty stash). BlockNote rich + source + preview 3-way toggle. Browser smoke e2e. Deferred: monorepo extraction.
2 β€” Inbox + AI βœ… write loop live Polling scan + notify watcher + Korean date parser + Claude CLI bridge + classifier + Gmail via gws, plus InboxPane (classify/accept/reject/process, approval gate, Maru labels, a/r/p, bulk actions, mission log tails).
2.5 β€” Tree + Cursor shell + Terminal βœ… shipped Documents/Files Explorer, VS Code-style file tree + copy/move queue, Cursor-style activity rail, split panes (⌘D), Rust-native alacritty_terminal PTY tabs (Claude/Codex/Shell), layered ~/.maru/settings.json + <workspace>/.maru/workspace-state.json, signed auto-update, native menu bar.
3 β€” Unified document ops (M1–M7) βœ… W1–W6 + skills SSOT Operations Catalog mode (ops_catalog::scan, fs watcher, Hub HTTP read + ETag/offline fallback, drilldown + Reveal), Hub Library client + template-aware new doc, Writing Guideline sidebar. Rust skill_host owns tiers (core/public/private/imported/managed), doctor validation, dirty/reconcile. maru-hub backs shared catalog (REST + Alembic + seeds).
4 β€” Document Studio + Templates βœ… W7–W12 7-step Studio mode (source β†’ template β†’ guideline β†’ sections β†’ HWP fields β†’ export β†’ package). create_document frontmatter prefill, M4 export pipeline (export/ plan/validate/dispatch, docx/hwpx/pdf + sha256 manifest), HWPX field map (hwpx slots + template_fill), κ°œμ‘°μ‹ inline lint (linter/gaejosik).
5 β€” Evidence Binder βœ… W13 shipped Right-pane Evidence Binder tab keyed by doc id, state under <workspace>/.maru/binder/<doc-id>.json, seeds from inbox-processed files + <binary>.evidence.yaml sidecars scoped to the active BU, kordoc_lite format detection + HWPX field previews. W14–W18 (section/KPI/checklist bindings + Deck Studio) planned.
D β€” Concept-map Diagram mode βœ… shipped (Phase 0–7, hardened) diagram mode: HWP-style 9-tab ribbon, 13 node kinds, 4-port edges, smart-guide snap, 11 templates, PNG/JPG/SVG/JSON/PDF/Mermaid export + Mermaid import, version history, viewport culling for 1000-node smoothness. Docs at <workspace>/diagrams/<name>.cmd.json (v:7). See docs/diagram.md.
8 β€” Knowledge graph βœ… 8a/8b/8c + V4 shipped graph mode: stable Sigma WebGL + Graphology multi-directed model, Barnes-Hut ForceAtlas2 worker, visibility reducers, 10k+ node target, knowledge/workspace/all-files sources, local depth/direction controls, background insights, reviewed relationship writes, incremental cache/watch refresh. Managed writes remain schema-gated, revision-checked, snapshotted, and atomic. See docs/graph.md.
M0 β€” Anchor β†’ Maru rename βœ… shipped (v0.3.0) Full rename across app id, dirs, CLI, tap. One-time on-disk migration (~/.anchor β†’ ~/.maru, com.anchor.app β†’ com.maru.app) with back-compat symlink; .maruignore preferred with .anchorignore fallback read.

Rule SSOTs live in the work repo at ~/workspace/work/_meta/rules/{frontmatter-schema,document-lifecycle,hub-contract,evidence-policy}.md. The deeper "what's next + how to continue" reference is ROADMAP.md.

Modes

The activity rail exposes eleven top-level modes (Settings opens as a separate window, so it is not an app mode). Diagram and Graph default on; E2E Flow is flag-gated.

Mode Label (ko / en) What it does
pkm λ¬Έμ„œ / Docs Default. Markdown editor + Documents/Files Explorer + right utility rail.
inbox μΈλ°•μŠ€ / Inbox Configured drop / pending / processed / Files / Gmail sections with classify + a/r/p.
comms λ©”μ‹œμ§€ / Messages Multichannel comms settings (Telegram auth/mapping, source config, macOS migration).
meetings 회의둝 / Meetings Transcript + auto-summary intake and the meeting-notes review workbench.
tasks νƒœμŠ€ν¬ / Tasks File-backed tasks with Google Tasks/Calendar links; edit details, month/week/day calendar.
catalog μΉ΄νƒˆλ‘œκ·Έ / Catalog M1 Operations Catalog β€” deadlines, in-flight approvals, unlinked evidence, inbox pending.
studio μŠ€νŠœλ””μ˜€ / Studio M2 Document Studio 7-step authoring wizard. See docs/studio.md.
diagram λ‹€μ΄μ–΄κ·Έλž¨ / Diagram Concept-map editor. See docs/diagram.md.
graph κ·Έλž˜ν”„ / Graph Vault knowledge graph. See docs/graph.md.
sites μ‚¬μ΄νŠΈ / Sites Left-rail site switcher with an embedded native browser pane.
e2e E2E ν”Œλ‘œμš° / E2E Flow Hidden end-to-end flow console (flag-gated e2eFlowEnabled).

Install

Maru ships the desktop app and CLI as separate artifacts. On macOS, both are distributed through the STAIxBWLB/homebrew-cask tap:

brew tap STAIxBWLB/homebrew-cask

# Desktop app only:
brew install --cask maru-workspace

# Standalone CLI only. Installs the executable as `maru`:
brew install maru-cli

maru --version

The app cask installs Maru.app and does not create a CLI symlink. The CLI formula installs only the standalone maru executable. The desktop app keeps using signed Tauri updater metadata from GitHub Releases; Homebrew users can also upgrade via brew upgrade --cask maru-workspace and brew upgrade maru-cli.

For repo-local management shortcuts:

make cli-install
make cli-smoke
make release-preflight
make homebrew-update RELEASE_TAG=v0.4.0 HOMEBREW_TAP_DIR=../homebrew-cask

Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Tauri Webview (src/)                                        β”‚
β”‚   React 19 + Radix UI + BlockNote + marked + DOMPurify       β”‚
β”‚   Sigma WebGL + Graphology Β· CodeMirror Β· alacritty canvas    β”‚
β”‚                                                               β”‚
β”‚   Activity rail (11 modes):                                  β”‚
β”‚     Docs / Inbox / Messages / Meetings / Tasks /             β”‚
β”‚     Catalog / Studio / Diagram / Graph / Sites / E2E         β”‚
β”‚   Explorer + editor tabs + copy/move queue + terminal panel   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                               β”‚ Tauri IPC
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Rust core (src-tauri/src/)                                  β”‚
β”‚   workspace scan β€” walkdir + .maruignore + cached index      β”‚
β”‚   frontmatter/   β€” line-by-line YAML edit (preserves order)  β”‚
β”‚   document.rs    β€” read/save/create/version + field patch    β”‚
β”‚   git.rs         β€” status/commit/diff via shell-out          β”‚
β”‚   vault_list.rs / vault.rs / vault_graph.rs / vault_guard.rs β”‚
β”‚   inbox.rs / inbox_watcher.rs / inbox_classifier.rs          β”‚
β”‚   gmail_gws.rs / outlook_mso.rs / telegram_io.rs / ai_router β”‚
β”‚   ops_catalog/ Β· hub_client/ Β· export/ Β· studio/ Β· diagram/  β”‚
β”‚   skill_host/ Β· agent_host/ Β· terminal/ Β· linter/            β”‚
β”‚   maru_dir.rs   β€” layered settings + .maru rules/templates   β”‚
β”‚   maru_migration.rs β€” one-time Anchor β†’ Maru on-disk rename  β”‚
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
       β”‚ stdio bridge
β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ MCP server    β”‚ β”‚ User's Claude Code β”‚
β”‚ (Node sidecar) β”‚ β”‚ CLI (~/.maru/skills)β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Module boundary rules:

  • Rust core owns workspace FS / cache / git / frontmatter / inbox scan/watch/classification / Gmail gws + Outlook + Telegram bridges / layered Maru settings / Claude inbox subprocess / integrated terminal PTY + screen model / skills registry (skill_host) / agent proposal host (agent_host) / ops catalog / export pipeline / Studio + Diagram + Graph backends.
  • React handles only BlockNote / command palette / neighborhood / graph layout worker / diagram canvas. No business logic.
  • Node sidecar (sidecars/maru-mcp/) holds the local read-first MCP server.
  • Deferred (Phase 4 original plan, on hold): a Whisper (Korean large-v3) Python sidecar + MediaPipe voice/gesture editing. Not shipped β€” Phase 4 was repurposed to Document Studio. Voice/gesture remains a future track.

Phase 3 verification gates (passed)

  1. Catalog watcher + auto-refresh β€” notify recursively watches inbox/items/, tasks/{active,calendar}, every BU's 02-admin-approvals/ + 03-evidence-cert/ + .maru/bu-config.yaml; bursts are debounced 500 ms and the React pane re-queries in another 300 ms.
  2. Hub catalog read path β€” hub_client::http::fetch_with_cache GETs /api/v1/{templates,guidelines,glossary,...} with ETag revalidation, falls back to <workspace>/.maru/cache/hub/ on any network error.
  3. Drilldown dialog + Reveal β€” Catalog row β†’ modal showing frontmatter + manifest + README excerpt + sibling paths + "Finderμ—μ„œ 보기".
  4. Real-workspace timing β€” MARU_CATALOG_BENCH_WORKSPACE=~/workspace/work cargo test --lib -- --ignored catalog_real_workspace_smoke indexed 110 entries across 4 BUs in 986 ms (30Γ— under the 30-second budget).
  5. Template-aware new doc β€” ⌘ ⇧ N / palette "Hub ν…œν”Œλ¦ΏμœΌλ‘œ μƒˆ λ¬Έμ„œ" β†’ BU/category filter β†’ template picker prefills body + title + docType β†’ optional guideline multi-select β†’ maru:template / maru:business_unit / maru:guidelines provenance.
  6. Guideline sidebar β€” opening a doc created via the template flow surfaces its guideline bodies in the right-pane BookOpen tab.

Live-Hub verification (operator procedure)

The above gates run fully offline (cache fallback). To exercise the live Hub read path:

# maru-hub: start FastAPI + sqlite (or run docker compose up -d db for Postgres)
cd dev/maru-hub
uv run python -m scripts.seed_catalog
MARU_HUB_DATABASE_URL=sqlite:///tmp/maru-hub.db uv run uvicorn maru_hub.main:app --port 8017

# maru: flip workspace.config.yaml
#   hub.enabled: true
#   hub.endpoint: http://127.0.0.1:8017/api/v1
#   hub.api_token_ref: ~/workspace/work/.maru/secrets/hub-token (empty file is fine for local)

Then in Maru: open Catalog mode β†’ footer "λ§ˆμ§€λ§‰ μŠ€μΊ”" populates; ⌘ ⇧ N β†’ toggle "Hub ν…œν”Œλ¦Ώμ—μ„œ μ‹œμž‘" β†’ templates load from Hub; pick business-plan-default β†’ body prefills with slot hints.

After Phase 6 W21 ships, the same operator procedure will also exercise the POST /api/v1/documents/{id}/finalize round-trip β€” Maru pushes the approved markdown body + rendered artifacts + linked evidence binaries to Hub, and the document then appears under the Hub Finalized tab inside Catalog mode.

Roadmap

The active plan lives in ROADMAP.md β€” a 7-module (M1–M7) decomposition with weekly deliverables (W1–W34+) plus the Diagram and Graph side tracks. Phases 0–5, the Diagram mode, and the Phase 8 graph mode (8a/8b/8c) are shipped. See CHANGELOG.md for the release-by-release history.

Each phase is defined in outcomes the user actually exercises. No phase exists just to grow infrastructure. The entry gate for each phase is the verification of the previous one.

Next up

Git has run ahead of the linear W-plan: Phase 8 (graph mode) shipped before the remaining Phase 5 evidence work. The nearest pending items:

  1. W14–W15 Evidence bindings β€” section/KPI/submission-checklist binding model on the W13 Binder (evidence_links[].section_bindings, kpi_bindings, checklist bindings), Verify / Mark-as-submitted controls, then Hub evidence_index sha256 reuse. Entry files: src-tauri/src/evidence_binder.rs, src/components/evidence/*, src/lib/evidenceBinder.ts.
  2. W16–W18 Deck Studio (M6) β€” a Decks mode wrapping the gpt-images-deck wizard with the bundled 14-style catalog (skills/docs/slide-decks/).
  3. Phase 6 (W19–W22) Approval + Finalize-to-Hub β€” submission gates, gate-state polling, and the approval-gated POST /api/v1/documents/{id}/finalize write path (the only Maru client path allowed to carry body/binary payloads). Requires a matching hub_client/safety.rs pre-flight.
  4. Phase 7 (W23–W26) Certification & KPI bundle β€” Hub-backed certification vault, KPI composer, and PDF bundle assembly.

Hub as published-document SSOT

Maru stays the author SSOT β€” drafting and editing always happen under ~/workspace/work/. Maru Hub becomes the published SSOT the moment an approval route closes. Two write paths land on Hub from Maru:

  1. POST /api/v1/documents/sync (planned M7 caller) β€” drafting metadata only: document_uri, body_sha256, frontmatter, and the evidence link graph. No body, no binary. Used for cross-BU lookups and "이미 λ™κΈ°ν™”λœ μ΄ˆμ•ˆ" hints.
  2. POST /api/v1/documents/{id}/finalize (Phase 6 W21) β€” approval-gated canonical push. The instant submission_gate.state flips to approved, Maru auto-calls finalize with the full markdown body, every rendered artifact in the M4 manifest (docx/hwpx/pdf), and the binary bytes of every evidence file linked via frontmatter.evidence_links. On 201, the local frontmatter status flips to archived-hub:<finalized_id>@v<N>.

Gate submits made while the Hub is disabled or unreachable persist in <workspace>/.maru/queue/hub/ (one JSON per request) and drain FIFO via the hub_queue_drain command β€” exposed in the Catalog footer as a queue depth badge with a retry action.

Development

pnpm install

# Browser dev (mocked Tauri):
pnpm dev

# Native Tauri dev (cleans stale local app bundles first):
pnpm tauri:dev

# Type check:
pnpm typecheck

# i18n lint (ko/en parity + hardcoded UI string scan; also in make verify):
pnpm lint:i18n

# Production build:
pnpm build

# Full verification (typecheck + vitest + cargo test --lib + build):
make verify

# Signed native release build:
make tauri-build

# Raw pnpm build still requires explicit updater signing env:
export TAURI_SIGNING_PRIVATE_KEY="$(cat ~/.tauri/maru-updater.key)"
export TAURI_SIGNING_PRIVATE_KEY_PASSWORD="$(cat ~/.tauri/maru-updater.key.password)"
pnpm tauri:build

# Prune oversized local Tauri debug artifacts (also runs from tauri:dev/build):
pnpm clean:tauri-debug
# Checks once every 24h and prunes src-tauri/target/debug when artifacts exceed 4GiB.

# Rust unit + integration tests (587 declarations; 2 ignored benchmarks):
cd src-tauri && cargo test
# or: make test-rust  (cargo test --lib)

# Frontend unit tests (vitest, 87 test files / 638 tests):
pnpm test
# End-to-end (Playwright, 7 specs):
pnpm test:e2e

# Local Maru MCP sidecar smoke:
MARU_MCP_WORKSPACE="$PWD" node sidecars/maru-mcp/index.mjs

# Skills registry doctor / reconcile:
cargo run --manifest-path src-tauri/Cargo.toml --bin maru-cli -- --version
cargo run --manifest-path src-tauri/Cargo.toml --bin maru-cli -- doctor --json
cargo run --manifest-path src-tauri/Cargo.toml --bin maru-cli -- skills dirty --json
cargo run --manifest-path src-tauri/Cargo.toml --bin maru-cli -- skills reconcile <name-or-id> --accept --dry-run
cargo run --manifest-path src-tauri/Cargo.toml --bin maru-cli -- skills import /path/to/skill --copy

# Bench workspace scan on a real workspace:
cd src-tauri && cargo test --release bench_scan_real_workspace \
    -- --ignored --nocapture --test-threads=1
# β†’ MARU_BENCH_WORKSPACE=/some/path overrides the default ~/workspace/work

CI runs make verify (typecheck + vitest + cargo test --lib + build) on every pull request and push to main via .github/workflows/ci.yml; pushes that touch only skills/** run make skills-verify instead of the full app toolchain. The heavier release-preflight (adds CLI + e2e + a debug Tauri build) runs on version tags.

Skills Bundle Channel (OTA)

Skills deploy independently of app releases. Merging skills/** changes to main triggers .github/workflows/release-skills.yml: it verifies and packages the tree (make skills-verify / make skills-package), signs the zip and metadata with the Tauri updater key, and uploads immutable assets to the fixed skills-channel prerelease. The app checks that channel at launch and applies new bundles automatically when the local skills are clean and runtime-compatible; maru skills update --check|--apply [--repair-env] and the Skills UI cover manual flows. The binary embeds only a frozen src-tauri/skills-bootstrap/ snapshot as the offline first-run fallback, so editing skills/ requires no Rust rebuild.

Release Bundles

Publishing a GitHub Release (a v* tag; the skills channel is excluded) triggers .github/workflows/release-bundles.yml. The workflow builds native Tauri bundles on macOS, Ubuntu, and Windows, then uploads the generated .app / .dmg, .deb / .rpm / .AppImage, .exe, and .msi assets to that same release. It also uploads signed updater metadata consumed by the startup auto-updater and native Check for Updates... menu action. A separate macOS CLI job builds maru-cli, packages it as a tarball containing an maru executable, and uploads maru-cli_<version>_darwin_{aarch64,x86_64}.tar.gz plus SHA256 files to the same release.

macOS bundles must be code signed before publishing. Until Apple Developer ID secrets are configured, Maru uses explicit ad-hoc bundle signing (bundle.macOS.signingIdentity = "-") so Apple Silicon downloads are not shipped as unsigned/broken app bundles. For fully trusted Gatekeeper launches, configure these GitHub Secrets and publish a new release:

  • APPLE_CERTIFICATE β€” base64 encoded Developer ID Application .p12
  • APPLE_CERTIFICATE_PASSWORD
  • KEYCHAIN_PASSWORD
  • APPLE_API_ISSUER_ID
  • APPLE_API_KEY_ID
  • APPLE_API_KEY β€” base64 encoded App Store Connect API .p8

The release workflow imports APPLE_CERTIFICATE only inside the macOS signing prep step, and it sends Apple notarization env vars only to the Developer ID build branch. It intentionally does not pass unset Apple secrets into tauri-apps/tauri-action, because empty environment variables make Tauri try to import or notarize with blank credentials.

Minimum Apple Developer setup for direct distribution:

  1. In Apple Developer Certificates, Identifiers & Profiles, create only a Developer ID Application certificate. Maru does not need an Identifier or Provisioning Profile for the current direct-distribution path because it does not use iCloud, Push Notifications, App Groups, or another advanced entitlement that requires a Developer ID provisioning profile.

  2. Install the downloaded .cer into Keychain Access, then export it with its private key as a password-protected .p12.

  3. Encode the .p12 and set the release secrets:

    tmp_cert_b64="$(mktemp)"
    openssl base64 -A -in DeveloperIDApplication.p12 -out "$tmp_cert_b64"
    gh secret set APPLE_CERTIFICATE --repo STAIxBWLB/maru --body-file "$tmp_cert_b64"
    rm "$tmp_cert_b64"
    
    gh secret set APPLE_CERTIFICATE_PASSWORD --repo STAIxBWLB/maru
    gh secret set KEYCHAIN_PASSWORD --repo STAIxBWLB/maru
    gh secret set APPLE_API_ISSUER_ID --repo STAIxBWLB/maru
    gh secret set APPLE_API_KEY_ID --repo STAIxBWLB/maru
    gh secret set APPLE_API_KEY --repo STAIxBWLB/maru
  4. Confirm release readiness without printing secret values:

    make macos-distribution-check
    make macos-distribution-local-check

For a local notarization smoke test, keep Apple files under ~/workspace/work/.maru/secrets/apple/:

  • DeveloperIDApplication.p12
  • AuthKey_<APPLE_API_KEY_ID>.p8
  • certificate-password
  • api-issuer-id
  • optional api-key-id (defaults to the AuthKey_<id>.p8 filename)
  • optional keychain-password (generated locally if missing)

Then run:

make macos-notarize-local TARGET=aarch64-apple-darwin

Keep the Tauri updater secrets (TAURI_SIGNING_PRIVATE_KEY, TAURI_SIGNING_PRIVATE_KEY_PASSWORD) in place; they sign updater metadata and are separate from Apple Developer ID signing. The workflow fails on partial Apple signing configuration instead of silently producing an unintended ad-hoc macOS release.

Release asset versions come from the app metadata in package.json, src-tauri/tauri.conf.json, and src-tauri/Cargo.toml; keep those in sync before tagging or publishing a release. After release assets exist, update the Homebrew tap with:

make homebrew-update-commit RELEASE_TAG=v0.4.0 HOMEBREW_TAP_DIR=../homebrew-cask
make homebrew-audit HOMEBREW_TAP_DIR=../homebrew-cask
make homebrew-fetch HOMEBREW_TAP_DIR=../homebrew-cask

After downloading the release DMG, verify Gatekeeper-facing state on macOS:

xcrun stapler validate Maru_*.dmg
spctl -a -vv -t open --context context:primary-signature Maru_*.dmg
codesign --verify --deep --strict --verbose=4 /Applications/Maru.app
spctl -a -vv -t exec /Applications/Maru.app

Workspace Layout

An AI workspace is any folder containing .md (or .markdown, .html, .htm) files.

Private workspace is the required default. Public workspace is optional and means a provider-managed shared root, not internet publishing. V1 capability support is registry-only: Maru stores non-secret provider metadata in workspaces.json, maps a manually entered provider role to coarse capabilities, intersects that with a filesystem writability probe, and gates direct writes in the UI and Rust commands. OAuth, Microsoft Graph, Google Drive, and Nextcloud live API checks are deferred.

Supported public providers are Local, Google Drive, OneDrive, SharePoint, Nextcloud, Obsidian, and Unknown. workspace.config.yaml accepts:

paths:
  private: ~/workspace/work
  public:
    - label: Team Drive
      path: ~/gdrive-workspace/work
      provider: googleDrive
      providerId: shared-drive-id
      writePolicy: direct
      role: contentManager
    - label: Reference Site
      path: ~/shared/reference
      provider: sharePoint
      writePolicy: readOnly
      role: Can view

Maru stores user/global preferences at:

~/.maru/
  settings.json    # UI/theme/layout/window/split/terminal/explorer/file-queue/AI defaults

Maru stores workspace-local state and resources at:

<workspace>/
  .maru/
    cache/           # disposable workspace index for warm startup
    workspace-state.json # collapsed folders, initialization flags, binary patterns, overrides
    versions/        # snapshots created via the "Version" button
    studio/          # per-document Studio wizard state
    binder/          # per-document Evidence Binder state
  .maruignore      # optional, gitignore-style segment patterns (falls back to legacy .anchorignore)

<workspace>/.maru/settings.json is a legacy migration input only. Maru reads it when present to build effective settings, but new workspaces do not get that file.

.maruignore example for the user's ~/workspace/work:

node_modules
.venv
dist
build
target
.next
.turbo
.cache
.maru/cache

Critical invariants

  1. Filesystem is authoritative. The fingerprinted cache (<workspace>/.maru/cache/workspace-index-v3.json) is disposable. Warm reconciliation reuses unchanged entries and reparses changed files only; React state remains derived.
  2. Frontmatter key order + comments preserved. A single-field patch must never disturb the order or comments of any other key (verified by cargo test). src-tauri/src/frontmatter/ops.rs is the only allowed write path.
  3. Crash-safe rename. .maru-rename-txn/ staging dir + recovery on the next workspace scan.
  4. Dynamic relationship detection. Any frontmatter field containing [[wikilink]] is treated as a relationship. No hard-coded field lists.
  5. Symlinks inside the workspace are honored. Deliberate user-created symlinks (e.g. ~/workspace/work/inbox/downloads β†’ ~/gdrive-workspace/...) are considered part of the workspace. Maru uses lexical containment, not canonicalize().
  6. Managed vault writes are schema-gated + snapshotted. write_policy: "managed" writes pass vault_guard::validate_managed_write and take a snapshot before mutation; note deletion stays MCP-only.

Hard "No" list (v1)

Out of scope for v1 by explicit decision:

  • Semantic / embedding search (keyword + wikilink + git-grep cover 10k notes).
  • Cloud sync, maru account, default telemetry (opt-in only).
  • Mobile (Tauri 2 mobile is unstable; Obsidian owns mobile for now).
  • Public marketplace server (cloned sources carrying a maru.source.json manifest are schema-validated on install and rolled back on failure; the manifest signed flag is a metadata check, not cryptographic signature verification β€” no server, no moderation policy).
  • iMessage / Slack ingestion (permission pain > value).
  • Multi-user collab, CRDT, realtime (single user, single device, git for history).
  • PDF annotation, OCR (file-extracted text is enough).
  • Agent-autonomous edits as default behavior. Autonomy is staged behind disposable workspaces, protected writes, approval policy, and audit events.
  • iCloud / Dropbox workspace awareness (user's responsibility).
  • Unsigned / ad-hoc auto-updater feeds (updates are accepted only through signed GitHub Release artifacts).

License

No license file is currently published. All rights reserved unless a license is added.

About

Local-first Maru Workspace and AI editing desktop app

Resources

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors