Skip to content

chrischabot/notes-to-walrus-blog

Repository files navigation

Notes Publisher

A macOS app that publishes Apple Notes to decentralized Walrus Sites.

Write in Apple Notes. Sign in with Apple. Click Publish. Blog is live on Walrus!

Overview

Notes Publisher is a SwiftUI macOS app that:

  1. Reads notes from a folder in Apple Notes via AppleScript
  2. Generates static HTML with iCloud Notes styling
  3. Publishes to Walrus Sites using zkLogin-signed transactions
  4. Uses Enoki zkLogin with Apple Sign-In for wallet management - no seed phrases, no extensions

Platform: macOS only (Apple Notes has no public API on iOS)

Key Design Decision: No external CLI dependencies. The app bundles Node.js and uses HTTP APIs + the @mysten/sui SDK for all blockchain operations.

Quick Start

View Debug Logs

To view debug logs while the app is running:

log stream --predicate 'subsystem == "com.walrus.notes-publisher"' --level debug

1. Setup Apple Notes

Create a folder called "Blog" in Apple Notes and add some notes:

  • First line becomes the title
  • Rest is the body content
  • Formatting (bullet points, links, bold/italic) is preserved

2. Build and Run

Important: You must use Xcode, not swift build. Sign in with Apple requires proper code signing.

# Generate Xcode project (if not already present)
xcodegen generate

# Open in Xcode
open NotesPublisher.xcodeproj

In Xcode:

  1. Select your Development Team in Signing & Capabilities
  2. Build and run (Cmd+R)

3. Sign In & Publish

  1. Click "Sign in with Apple" to create your Sui wallet
  2. Get testnet tokens using the links in the app
  3. Select your notes folder
  4. Click "Publish to Walrus" or "Generate HTML Only"

Architecture

┌─────────────────────────────────────────────────────────┐
│                    Notes Publisher                       │
├─────────────────────────────────────────────────────────┤
│  Enoki zkLogin       │  AppleScript                     │
│  (Apple Sign-In)     │  (Apple Notes access)            │
├──────────────────────┴──────────────────────────────────┤
│                     HTMLGenerator                        │
│  (Static HTML with iCloud Notes CSS, light/dark mode)   │
├─────────────────────────────────────────────────────────┤
│                  Publishing Pipeline                     │
│  ┌──────────────┐  ┌───────────────┐  ┌─────────────┐  │
│  │ WalrusPublisher│  │  SuiBridge    │  │  SitePublisher│ │
│  │ (HTTP API)    │  │  (Node.js)    │  │ (Orchestrator)│ │
│  └──────────────┘  └───────────────┘  └─────────────┘  │
└─────────────────────────────────────────────────────────┘
         │                    │                   │
         ▼                    ▼                   ▼
    Walrus HTTP          Node.js SDK         zkLogin
    Blob Storage         Transactions        Signing
         │                    │                   │
         └────────────────────┼───────────────────┘
                              ▼
                ┌─────────────────────────┐
                │      Walrus Sites       │
                │  https://xyz.walrus.site │
                └─────────────────────────┘

Publishing Flow

  1. Store Blobs - Upload HTML/CSS/JS files to Walrus via HTTP API
  2. Get zkProof - Request zero-knowledge proof from Enoki
  3. Build Transaction - Use Node.js + @mysten/sui SDK to create site transaction
  4. Sign with zkLogin - Combine ephemeral signature with zkProof
  5. Execute - Submit signed transaction to Sui network
  6. Done - Site is live at https://{object_id}.walrus.site

Features

  • Enoki zkLogin: Sign in with Apple to get a Sui wallet - no seed phrases, no extensions
  • Network Selection: Switch between testnet and mainnet in settings
  • iCloud Notes Styling: Generated HTML matches the look of iCloud Notes
  • Light/Dark Mode: Automatically follows system preference
  • Customizable Templates: Edit CSS and HTML templates in ~/Library/Application Support/NotesPublisher/Templates
  • Local Preview: Generate HTML without publishing to preview your site
  • One-Click Publish: Read notes, generate HTML, and upload in a single action

Configuration

Settings (Gear icon)

Setting Description Default
Network Sui network (testnet/mainnet) testnet
Blog Title Site title on index page "My Notes Blog"
Author Name Author name in footer ""

Custom Templates

Customize your blog's appearance by editing templates:

  1. Click "Open Templates" in Settings
  2. Edit styles.css, index.html, or note.html
  3. Templates use placeholders like {{TITLE}}, {{CONTENT}}, {{NOTES_LIST}}

Templates are created in ~/Library/Application Support/NotesPublisher/Templates/ on first run.


Development

Project Structure

notes-blog/
├── Package.swift                    # Swift package manifest
├── project.yml                      # xcodegen configuration
├── NotesPublisher.xcodeproj         # Generated Xcode project
├── Shared/
│   ├── App/NotesPublisherApp.swift  # Entry point
│   ├── Design/Theme.swift           # UI theme
│   ├── Models/
│   │   ├── AppSettings.swift        # User settings + SuiNetwork
│   │   ├── CLITool.swift            # CLI tool enum (Node.js only)
│   │   └── PublishingState.swift    # Flow states
│   ├── ViewModels/
│   │   └── DashboardViewModel.swift # Main state
│   ├── Views/
│   │   ├── Dashboard/               # Main dashboard
│   │   ├── Components/              # Reusable UI components
│   │   └── Settings/                # Settings view
│   └── Services/
│       ├── Auth/
│       │   ├── EnokiService.swift   # Enoki zkLogin + session data
│       │   └── KeychainHelper.swift # Secure storage
│       ├── CLI/
│       │   └── CLIBinaryResolver.swift # Node.js path resolution
│       ├── NotesService/            # Apple Notes access
│       ├── Publishing/
│       │   ├── HTMLGenerator.swift  # HTML generation
│       │   ├── AppDirectories.swift # Template/output folders
│       │   ├── WalrusPublisherService.swift # HTTP blob storage
│       │   ├── SuiBridgeService.swift # Node.js bridge
│       │   ├── SitePublisherService.swift # Publishing orchestrator
│       │   └── PublishingCoordinator.swift
│       └── SuiService/              # Sui GraphQL queries
├── macOS/                           # macOS-specific files
│   ├── NotesPublisher.entitlements  # Sign in with Apple entitlement
│   └── Info.plist                   # App metadata
└── Resources/
    └── CLI/
        ├── node/                    # Bundled Node.js (arm64, x86_64)
        ├── scripts/
        │   ├── sui-bridge.js        # Transaction bridge
        │   └── package.json         # @mysten/sui dependency
        └── config/
            └── networks.json        # Network configuration

Building

# Generate Xcode project
xcodegen generate

# Restore entitlements (xcodegen clears them)
# The entitlements file at macOS/NotesPublisher.entitlements must contain:
# - com.apple.developer.applesignin
# - keychain-access-groups

# Open in Xcode
open NotesPublisher.xcodeproj

# Build and run: Cmd+R

Why Xcode Instead of SPM?

Sign in with Apple requires:

  • A properly signed .app bundle
  • Developer certificate code signing
  • The com.apple.developer.applesignin entitlement

swift build creates unsigned executables that can't use Sign in with Apple.


Apple Sign-In + Enoki Setup

For detailed setup instructions, see blog.md.

Quick Checklist

Apple Developer Portal:

  1. Create App ID with "Sign in with Apple" capability
  2. Create Services ID
  3. Configure Services ID with:
    • Domain: api.enoki.mystenlabs.com
    • Return URL: https://api.enoki.mystenlabs.com/v1/zklogin/callback/apple

Enoki Portal:

  1. Create project at portal.enoki.mystenlabs.com
  2. Add Apple auth provider
  3. Add your Bundle ID as allowed client ID (not Services ID!)

Xcode:

  1. Set Bundle ID to match Apple Developer setup
  2. Select Development Team
  3. Add "Sign in with Apple" capability

Troubleshooting

"Apple Notes access denied"

Grant automation access: System Preferences > Privacy & Security > Automation > Enable Notes for your app

"Sign in with Apple failed" / ASAuthorizationError 1000

  • Using swift build instead of Xcode? Sign in with Apple requires Xcode builds
  • Check entitlements include com.apple.developer.applesignin
  • Ensure Development Team is selected in Xcode

"Invalid client ID" from Enoki

Your Bundle ID must be added as an allowed client ID in Enoki Portal. For native apps, the JWT audience is the Bundle ID, not the Services ID.

"Ed25519 public key invalid"

The ephemeral public key needs a 0x00 scheme flag prefix. This is handled in EnokiService.swift.

"No notes found"

  1. Create a folder in Apple Notes matching the name in settings (default: "Blog")
  2. Add at least one note to the folder
  3. Grant Notes access when prompted

Resources

Design Specs:

External Docs:


License

MIT

About

Publishes Apple Notes to a Walrus decentralised site

Topics

Resources

Stars

Watchers

Forks

Contributors