Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
name: Release

# Cut a release by pushing a version tag, e.g.
# git tag v2.0.1 && git push origin v2.0.1
on:
push:
tags:
- 'v*'
workflow_dispatch:

jobs:
release:
permissions:
contents: write
runs-on: macos-latest
steps:
- uses: actions/checkout@v4

- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: 20

- name: Set up Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: aarch64-apple-darwin,x86_64-apple-darwin

- name: Cache cargo
uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri

- name: Install frontend dependencies
run: npm install

- name: Build, sign, notarize & publish
uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# --- Code signing (Developer ID Application) ---
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
# --- Notarization (Apple ID + app-specific password) ---
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
# --- Updater artifact signing ---
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
Comment on lines +50 to +51

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Use Tauri 2 updater signing environment variables

For tagged releases, this step passes the old updater key names, but Tauri 2's bundler reads TAURI_SIGNING_PRIVATE_KEY and TAURI_SIGNING_PRIVATE_KEY_PASSWORD. Because src-tauri/tauri.conf.json now contains an updater pubkey, the release build will not see the private key under the names it checks, so updater artifacts/signatures won't be produced correctly and the release job can fail.

Useful? React with 👍 / 👎.

with:
tagName: ${{ github.ref_name }}
releaseName: 'RocketNote ${{ github.ref_name }}'
releaseBody: 'Download the .dmg below. The app is signed and notarized; existing installs update automatically.'
releaseDraft: true
prerelease: false
# Universal binary (Apple Silicon + Intel). tauri-action emits the
# signed updater bundle + latest.json and attaches them to the release.
args: --target universal-apple-darwin
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,7 @@ backup-*/
icon.iconset/
fix-icons.sh
generate-icons.sh
src-tauri/Entitlements.plist
src-tauri/Info.plist

# Tauri v2 generated schemas
src-tauri/gen/
30 changes: 30 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Changelog

All notable changes to RocketNote are documented here.
Format based on [Keep a Changelog](https://keepachangelog.com/).

## [Unreleased]

### Added
- **CodeMirror 6 editor** with true viewport virtualization — multi-megabyte logs and minified bundles open without freezing the UI.
- **Auto-updater** — signed update channel served from GitHub Releases (Tauri updater + `latest.json`).
- **Signed & notarized macOS builds** — GitHub Actions release pipeline (`.github/workflows/release.yml`) with Developer ID signing and notarization. See [docs/distribution-setup.md](docs/distribution-setup.md).
- **PKCE (S256)** on the cloud OAuth authorization-code flow for all providers.

### Changed
- **Migrated to Tauri 2** — capabilities/ACL permission model, split plugins, v2 menu API.
- Production bundle is **code-split** into cacheable vendor chunks (app-shell entry ~174 kB instead of a single ~1.7 MB chunk).
- Secrets are stored in the macOS Keychain via the **native Security framework** instead of the `security` CLI, so secret values never appear on the process command line.

### Security
- **Git:** every git invocation is hardened (`GIT_CONFIG_NOSYSTEM`, `core.fsmonitor` / `core.hooksPath` disabled, `ext`/`file` transports blocked) — opening an untrusted repository can no longer execute code via its `.git/config`.
- **Terminal:** script interpreters removed from the quick-run allowlist; `pty_write_force` only accepts confirmation keystrokes for a pending danger prompt (one-shot).
- **Filesystem:** snippet/macro ids are sanitized; `validate_path` denies credential locations (`~/.ssh`, `~/.aws`, `~/.gnupg`, `~/Library/Keychains`, …); `file_exists` is scope-checked; read and global-search size caps prevent memory-exhaustion hangs.
- **Cloud / Git:** OAuth token material is redacted from error messages and logs; git branch/ref names are guarded against option injection.

### Fixed
- License metadata in `Cargo.toml` aligned to GPL-3.0 (matches `LICENSE`).
- Broken README header image.

## [2.0.0]
- Initial public release — encryption, cloud sync, AI assistant, Git, terminal, global search, developer tools.
23 changes: 13 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Most code editors either collect your data or require an internet connection. Ro
- **Military-grade encryption** — AES-256-GCM with Argon2id key derivation
- **100% local** — your files never leave your device unless you choose to sync
- **Native performance** — built with Rust and Tauri, not Electron
- **Signed, notarized & auto-updating** — verified macOS builds that update themselves over a signed channel

---

Expand Down Expand Up @@ -53,12 +54,12 @@ Most code editors either collect your data or require an internet connection. Ro

Explain, improve, fix bugs, refactor code — all with your own API key stored in macOS Keychain.

### Code Editor
### Code Editor (CodeMirror 6)
- **Viewport virtualization** — opens multi-megabyte logs and minified bundles without freezing
- Syntax highlighting for 30+ languages
- Minimap with virtualized rendering
- Bracket matching, word wrap, line numbers (absolute & relative)
- Minimap, bracket matching, word wrap, line numbers
- Split view and focus mode
- Undo/redo with full history stack
- Native undo/redo history, find & replace

### Git Integration (⌘⇧G)
- Stage, commit, push, pull
Expand Down Expand Up @@ -128,26 +129,28 @@ Download the latest `.dmg` from [Releases](https://github.com/Anic888/rocketnote

### Build from Source

**Prerequisites:** macOS 10.13+, [Node.js](https://nodejs.org/) 18+, [Rust](https://rustup.rs/), Tauri CLI v1
**Prerequisites:** macOS 10.13+, [Node.js](https://nodejs.org/) 18+, [Rust](https://rustup.rs/)

```bash
git clone https://github.com/Anic888/rocketnote.git
cd rocketnote
npm install
cargo install tauri-cli --version "^1"
npm install # also installs the Tauri 2 CLI

cargo tauri dev # development
cargo tauri build # production .app
npm run tauri dev # development
npm run tauri build # production .app
```

Signing, notarization and the auto-updater are wired in `.github/workflows/release.yml` — see [docs/distribution-setup.md](docs/distribution-setup.md).

---

## Tech Stack

| Layer | Technology |
|-------|-----------|
| Frontend | React 18, TypeScript |
| Backend | Rust, Tauri 1.5 |
| Editor | CodeMirror 6 (virtualized) |
| Backend | Rust, Tauri 2 |
| Terminal | portable-pty |
| Encryption | AES-256-GCM, Argon2id |
| Search | Rayon (parallel), ignore (gitignore) |
Expand Down
Binary file added app-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
84 changes: 84 additions & 0 deletions docs/distribution-setup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# RocketNote — Distribution Setup (signing · notarization · auto-update)

This closes audit findings **C4** (unsigned/un-notarized macOS build) and **H3** (no update channel). Everything below is wired in `.github/workflows/release.yml`; you only need to add the GitHub **secrets** and cut a tag.

> The build is signed + notarized **in CI** with your Apple credentials. Nothing here can be verified on a machine without your Apple Developer account, so treat this as a checklist, not a finished build.

---

## 1. One-time: Apple code-signing identity

1. In the Apple Developer portal create a **Developer ID Application** certificate (this is the cert for apps distributed *outside* the App Store).
2. In **Keychain Access** → export it (with its private key) as a `.p12` file, set an export password.
3. Base64-encode it for the secret:
```sh
base64 -i DeveloperID.p12 | pbcopy
```
4. Find the exact identity string:
```sh
security find-identity -v -p codesigning
# e.g. "Developer ID Application: Your Name (TEAMID1234)"
```

## 2. One-time: notarization credentials

- Use your **Apple ID** + an **app-specific password** (appleid.apple.com → Sign-In and Security → App-Specific Passwords).
- Your **Team ID** is the 10-char code in the identity above / in the developer portal membership page.

## 3. Updater signing key — already generated ✅

A Tauri updater keypair was generated for you:

- **Public key** is already committed in `src-tauri/tauri.conf.json` → `updater.pubkey`.
- **Private key** is at `~/.tauri/rocketnote_updater.key` on this machine (it was created with an **empty** password).

Add its *contents* as the `TAURI_PRIVATE_KEY` secret:
```sh
cat ~/.tauri/rocketnote_updater.key | pbcopy
```
> Prefer to control the key yourself? Regenerate with
> `cargo tauri signer generate --ci -w ~/.tauri/rocketnote_updater.key -f`,
> then paste the **new** public key into `updater.pubkey`. Keep the private key out of git.

## 4. GitHub repository secrets

Settings → Secrets and variables → Actions → **New repository secret**:

| Secret | Value |
|---|---|
| `APPLE_CERTIFICATE` | base64 of the `.p12` (step 1.3) |
| `APPLE_CERTIFICATE_PASSWORD` | the `.p12` export password |
| `APPLE_SIGNING_IDENTITY` | `Developer ID Application: Your Name (TEAMID)` |
| `APPLE_ID` | your Apple ID email |
| `APPLE_PASSWORD` | the app-specific password |
| `APPLE_TEAM_ID` | your 10-char Team ID |
| `TAURI_PRIVATE_KEY` | contents of `~/.tauri/rocketnote_updater.key` |
| `TAURI_KEY_PASSWORD` | empty string (the key has no password) |

`GITHUB_TOKEN` is provided automatically — do not add it.

## 5. Cut a release

```sh
git tag v2.0.1
git push origin v2.0.1
```

The workflow builds a **universal** (Apple Silicon + Intel) binary, signs it with Developer ID, notarizes + staples it, and creates a **draft** GitHub Release with:
- `RocketNote_*.dmg` — the installer users download;
- `RocketNote.app.tar.gz` + `RocketNote.app.tar.gz.sig` — the signed update bundle;
- `latest.json` — the updater manifest.

Review the draft, then **Publish**. Publishing makes it the `latest` release, which is exactly what `updater.endpoints` points to:
`https://github.com/Anic888/rocketnote/releases/latest/download/latest.json`.

## 6. How auto-update works after this

On launch the app fetches `latest.json`, compares versions, verifies the bundle signature against the embedded `pubkey`, and (because `updater.dialog = true`) prompts the user to install. Ship a fix by bumping the version in `package.json` + `src-tauri/tauri.conf.json` and pushing a new tag.

## Notes / gotchas

- Bump the version in **both** `package.json` and `src-tauri/tauri.conf.json` (`package.version`) before tagging; the tag and the config version should match.
- First notarization can take a few minutes; tauri-action waits for it.
- If notarization fails on "hardened runtime" entitlements, the exceptions live in `src-tauri/Entitlements.plist`.
- `package-lock.json` is git-ignored, so CI uses `npm install` (not `npm ci`).
31 changes: 29 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,34 @@
"tauri": "tauri"
},
"dependencies": {
"@tauri-apps/api": "^1.5.3",
"@codemirror/commands": "^6.10.4",
"@codemirror/lang-cpp": "^6.0.3",
"@codemirror/lang-css": "^6.3.1",
"@codemirror/lang-html": "^6.4.11",
"@codemirror/lang-java": "^6.0.2",
"@codemirror/lang-javascript": "^6.2.5",
"@codemirror/lang-json": "^6.0.2",
"@codemirror/lang-markdown": "^6.5.0",
"@codemirror/lang-php": "^6.0.2",
"@codemirror/lang-python": "^6.2.1",
"@codemirror/lang-rust": "^6.0.2",
"@codemirror/lang-sql": "^6.10.0",
"@codemirror/lang-xml": "^6.1.0",
"@codemirror/lang-yaml": "^6.1.3",
"@codemirror/language": "^6.12.3",
"@codemirror/search": "^6.7.1",
"@codemirror/state": "^6.7.0",
"@codemirror/theme-one-dark": "^6.1.3",
"@codemirror/view": "^6.43.2",
"@tauri-apps/api": "^2.11.1",
"@tauri-apps/plugin-clipboard-manager": "^2.3.2",
"@tauri-apps/plugin-dialog": "^2.7.1",
"@tauri-apps/plugin-fs": "^2.5.1",
"@tauri-apps/plugin-http": "^2.5.9",
"@tauri-apps/plugin-notification": "^2.3.3",
"@tauri-apps/plugin-shell": "^2.3.5",
"@tauri-apps/plugin-updater": "^2.10.1",
"codemirror": "^6.0.2",
"html-to-image": "^1.11.11",
"lucide-react": "^0.577.0",
"react": "^18.2.0",
Expand All @@ -23,7 +50,7 @@
"xterm-addon-web-links": "^0.9.0"
},
"devDependencies": {
"@tauri-apps/cli": "^1.5.14",
"@tauri-apps/cli": "^2.11.3",
"@types/react": "^18.2.55",
"@types/react-dom": "^18.2.19",
"@vitejs/plugin-react": "^4.2.1",
Expand Down
Loading