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!
Notes Publisher is a SwiftUI macOS app that:
- Reads notes from a folder in Apple Notes via AppleScript
- Generates static HTML with iCloud Notes styling
- Publishes to Walrus Sites using zkLogin-signed transactions
- 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.
To view debug logs while the app is running:
log stream --predicate 'subsystem == "com.walrus.notes-publisher"' --level debugCreate 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
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.xcodeprojIn Xcode:
- Select your Development Team in Signing & Capabilities
- Build and run (Cmd+R)
- Click "Sign in with Apple" to create your Sui wallet
- Get testnet tokens using the links in the app
- Select your notes folder
- Click "Publish to Walrus" or "Generate HTML Only"
┌─────────────────────────────────────────────────────────┐
│ 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 │
└─────────────────────────┘
- Store Blobs - Upload HTML/CSS/JS files to Walrus via HTTP API
- Get zkProof - Request zero-knowledge proof from Enoki
- Build Transaction - Use Node.js + @mysten/sui SDK to create site transaction
- Sign with zkLogin - Combine ephemeral signature with zkProof
- Execute - Submit signed transaction to Sui network
- Done - Site is live at
https://{object_id}.walrus.site
- 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
| 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 | "" |
Customize your blog's appearance by editing templates:
- Click "Open Templates" in Settings
- Edit
styles.css,index.html, ornote.html - Templates use placeholders like
{{TITLE}},{{CONTENT}},{{NOTES_LIST}}
Templates are created in ~/Library/Application Support/NotesPublisher/Templates/ on first run.
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
# 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+RSign in with Apple requires:
- A properly signed
.appbundle - Developer certificate code signing
- The
com.apple.developer.applesigninentitlement
swift build creates unsigned executables that can't use Sign in with Apple.
For detailed setup instructions, see blog.md.
Apple Developer Portal:
- Create App ID with "Sign in with Apple" capability
- Create Services ID
- Configure Services ID with:
- Domain:
api.enoki.mystenlabs.com - Return URL:
https://api.enoki.mystenlabs.com/v1/zklogin/callback/apple
- Domain:
Enoki Portal:
- Create project at portal.enoki.mystenlabs.com
- Add Apple auth provider
- Add your Bundle ID as allowed client ID (not Services ID!)
Xcode:
- Set Bundle ID to match Apple Developer setup
- Select Development Team
- Add "Sign in with Apple" capability
Grant automation access: System Preferences > Privacy & Security > Automation > Enable Notes for your app
- Using
swift buildinstead of Xcode? Sign in with Apple requires Xcode builds - Check entitlements include
com.apple.developer.applesignin - Ensure Development Team is selected in Xcode
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.
The ephemeral public key needs a 0x00 scheme flag prefix. This is handled in EnokiService.swift.
- Create a folder in Apple Notes matching the name in settings (default: "Blog")
- Add at least one note to the folder
- Grant Notes access when prompted
Design Specs:
- app-design.md - macOS SwiftUI app design
- html-design.md - Generated blog HTML/CSS
External Docs:
MIT