Skip to content
This repository was archived by the owner on Jan 13, 2026. It is now read-only.

Conversation

@evoleinik
Copy link

Summary

With the managed cloud service shutting down on December 31, 2025, this PR adds complete self-hosting support so users can run their own Ito servers and build custom clients.

Changes

  1. Remote server support (lib/window/ipcEvents.ts)

    • Health check now uses VITE_GRPC_BASE_URL when set, allowing connection to remote servers (e.g., via Tailscale)
    • Falls back to VITE_LOCAL_SERVER_PORT for local development
  2. GitHub Actions workflow (.github/workflows/selfhosted-build.yml)

    • Users can fork the repo and build their own client via Actions
    • Input: server URL (e.g., http://myserver:3001)
    • Output: macOS DMG (arm64 or x64)
    • No secrets required - uses ad-hoc signing
  3. Documentation (README.md)

    • Complete self-hosting guide: server setup, client build, installation
    • Network setup recommendations (Tailscale, VPN)
    • Server auto-start configuration

User Experience

  1. Fork the repo
  2. Go to Actions → "Build Self-Hosted"
  3. Enter server URL
  4. Download DMG, re-sign, install

Testing

  • Tested with server running on remote Linux machine via Tailscale
  • Client successfully connects and transcribes

🤖 Generated with Claude Code

evoleinik and others added 3 commits December 13, 2025 20:38
The health check now uses VITE_GRPC_BASE_URL when set, allowing the app
to connect to remote servers (e.g., via Tailscale) instead of requiring
localhost. Falls back to VITE_LOCAL_SERVER_PORT for local development.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Complete guide for running a self-hosted Ito setup including:
- Server setup with Docker and database migrations
- Building client with custom server URL
- Installing and re-signing the app
- Network setup (Tailscale recommended)
- Server auto-start configuration

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Users can now build their own self-hosted Ito client via GitHub Actions:
- Fork the repo
- Run "Build Self-Hosted" workflow
- Enter their server URL
- Download the DMG artifact

Supports macOS arm64 and x64 builds. No secrets required.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@coderabbitai
Copy link

coderabbitai bot commented Dec 13, 2025

Walkthrough

This pull request adds self-hosting infrastructure and documentation to the project. A new GitHub Actions workflow (selfhosted-build.yml) enables building self-hosted macOS applications for both arm64 and x64 architectures, with steps for dependency installation, Rust native module compilation, environment configuration, Electron app building, DMG packaging, and artifact uploading. The README is updated with a new "Self-Hosting" section explaining remote server setup, configuration, and client installation. Additionally, the server health check logic in lib/window/ipcEvents.ts is updated to support configurable server URLs with fallback behavior and clarified error messaging.

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: adding self-hosting support with remote server connectivity and a GitHub Actions build workflow for custom client builds.
Description check ✅ Passed The description is directly related to the changeset, providing clear context about the managed cloud service shutdown, specific changes made across three files, user experience workflow, and testing validation.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (9)
lib/window/ipcEvents.ts (1)

608-613: Consider adding a timeout for remote server health checks.

Remote servers may have higher latency than local servers. The fetch call currently has no timeout, which could cause the health check to hang indefinitely if the server is unreachable or slow to respond.

Apply this diff to add a timeout:

-      const response = await fetch(serverUrl, {
-        method: 'GET',
-      })
+      const controller = new AbortController()
+      const timeoutId = setTimeout(() => controller.abort(), 10000) // 10 second timeout
+      
+      const response = await fetch(serverUrl, {
+        method: 'GET',
+        signal: controller.signal,
+      })
+      
+      clearTimeout(timeoutId)
README.md (3)

273-274: Clarify security implications of REQUIRE_AUTH=false.

While setting REQUIRE_AUTH=false is appropriate for personal self-hosting, it's important to emphasize that this should only be used on private networks. Consider adding a security note.

Apply this diff to enhance the security guidance:

-   REQUIRE_AUTH=false           # For personal use
+   REQUIRE_AUTH=false           # For personal use on private networks only

309-317: Local build command may have shell escaping issues.

The multi-line environment variable syntax in the local build example may not work correctly in all shells. Consider providing a more portable format.

Apply this diff to provide a single-line format:

-# Build with remote server URL (replace with your server address)
-VITE_GRPC_BASE_URL=http://your-server:3001 \
-VITE_ITO_ENV=dev \
-CSC_IDENTITY_AUTO_DISCOVERY=false \
-bun run electron-vite build
+# Build with remote server URL (replace with your server address)
+VITE_GRPC_BASE_URL=http://your-server:3001 VITE_ITO_ENV=dev CSC_IDENTITY_AUTO_DISCOVERY=false bun run electron-vite build

333-335: Port forwarding security warning could be stronger.

The documentation mentions that port forwarding is "not recommended for security reasons," but this could be more explicit about the risks.

Apply this diff to strengthen the security warning:

-- **Port forwarding**: Expose port 3001 (not recommended for security reasons)
+- **Port forwarding**: Expose port 3001 (**strongly discouraged** - exposes your server to the public internet without authentication)
.github/workflows/selfhosted-build.yml (5)

5-17: Consider adding URL validation for server_url input.

The workflow accepts any string as server_url without validation. Invalid URLs could cause build failures later in the process. Consider adding a validation step or providing clearer input description with URL format examples.

Update the input description to be more prescriptive:

       server_url:
-        description: 'Your server URL (e.g., http://myserver:3001)'
+        description: 'Your server URL including protocol and port (e.g., http://192.168.1.100:3001 or http://myserver.tailscale.net:3001)'
         required: true
         type: string

43-50: Make Rust module builds more robust.

The cd commands assume a specific directory structure and use relative paths. If the build fails in one module, the script continues with potentially incorrect working directory for subsequent modules.

Apply this diff to make the builds more robust:

       - name: Build native Rust modules (arm64)
         run: |
           for module in global-key-listener audio-recorder text-writer active-application selected-text-reader; do
             echo "Building $module for arm64..."
-            cd native/$module
-            cargo build --release --target aarch64-apple-darwin
-            cd ../..
+            (cd "native/$module" && cargo build --release --target aarch64-apple-darwin) || exit 1
           done

This uses a subshell to ensure each build starts from the correct directory and exits immediately if any build fails.


69-74: Silent failure in rename step could cause confusion.

The || true appended to the mv command silently tolerates rename failures. If the rename fails, users will download a DMG with the default name instead of the expected name with server info, causing confusion.

Apply this diff to provide better error handling:

       - name: Rename artifact with server info
         run: |
-          # Extract hostname from URL for naming
           SERVER_HOST=$(echo "${{ inputs.server_url }}" | sed 's|.*://||' | sed 's|:.*||' | sed 's|/.*||')
-          mv dist/Ito-Installer.dmg "dist/Ito-selfhosted-${SERVER_HOST}-arm64.dmg" || true
+          if [ -f "dist/Ito-Installer.dmg" ]; then
+            mv dist/Ito-Installer.dmg "dist/Ito-selfhosted-${SERVER_HOST}-arm64.dmg"
+          else
+            echo "Warning: Ito-Installer.dmg not found"
+            ls -la dist/
+          fi
-          ls -la dist/*.dmg

76-80: Artifact upload path pattern could match unexpected files.

The upload step uses dist/*.dmg as the path pattern, which could match multiple DMG files if previous builds weren't cleaned up. This might upload unintended files.

Apply this diff to make the upload more specific:

       - name: Upload macOS DMG (arm64)
         uses: actions/upload-artifact@v4
         with:
           name: Ito-macOS-arm64
-          path: dist/*.dmg
+          path: dist/Ito-selfhosted-*-arm64.dmg

23-137: Consider using a matrix strategy to reduce duplication.

The two jobs (build-mac-arm64 and build-mac-x64) contain nearly identical steps with only minor differences in target architecture and artifact naming. A matrix strategy could reduce this duplication and make the workflow easier to maintain.

This is a structural suggestion for future improvement—the current approach is functional and clear, but a matrix-based approach would reduce code duplication by ~50%.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bdfbae0 and d117952.

📒 Files selected for processing (3)
  • .github/workflows/selfhosted-build.yml (1 hunks)
  • README.md (1 hunks)
  • lib/window/ipcEvents.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Always prefer console commands over log commands. Use console.log instead of log.info

Files:

  • lib/window/ipcEvents.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/always.mdc)

Never use empty catch statements

Files:

  • lib/window/ipcEvents.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/code-conventions.mdc)

**/*.{ts,tsx}: Follow standard, idiomatic TypeScript coding practices for structure, naming, and types
Avoid adding comments unless they explain complex logic or non-obvious decisions; well-written, self-explanatory code is preferred
Do not add comments that merely restate what the code does
Rely on comprehensive tests to document the behavior and usage of code rather than extensive comments within the code itself
Use kebab-case when naming directories, TypeScript, and other files

**/*.{ts,tsx}: Prefer interfaces over types for object definitions
Use type for unions, intersections, and mapped types
NEVER use any or as any types or coercion
Leverage TypeScript's built-in utility types
Use generics for reusable type patterns
Use PascalCase for type names and interfaces
Use camelCase for variables and functions
Use UPPER_CASE for constants
Use descriptive names with auxiliary verbs (e.g., isLoading, hasError)
Prefix interfaces for React props with 'Props' (e.g., ButtonProps)
Keep type definitions close to where they're used
Export types and interfaces from dedicated type files when shared
Co-locate component props with their components
Use explicit return types for public functions
Use arrow functions for callbacks and methods
Implement proper error handling with custom error types
Use function overloads for complex type scenarios
Prefer async/await over Promises
Prefer function declarations over function expressions
Use readonly for immutable properties
Leverage discriminated unions for type safety
Use type guards for runtime type checking
Implement proper null checking
Avoid type assertions unless necessary
Handle Promise rejections properly

Files:

  • lib/window/ipcEvents.ts
lib/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Shared TypeScript modules, preload logic, and unit tests should be organized in lib/

Files:

  • lib/window/ipcEvents.ts
{app,lib}/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use Prettier with 2-space indent for code formatting across TypeScript and React files

Files:

  • lib/window/ipcEvents.ts
{app,lib,server}/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

{app,lib,server}/**/*.{ts,tsx,js,jsx}: Use ESLint to enforce code style and run bun run lint before submitting code
Always use console commands instead of log commands (e.g., console.log instead of log.info)

Files:

  • lib/window/ipcEvents.ts
{app,lib}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

{app,lib}/**/*.{ts,tsx}: Components and classes use PascalCase naming convention
Hooks and utility functions use camelCase naming convention

Files:

  • lib/window/ipcEvents.ts
{app,lib,server}/**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (AGENTS.md)

Constants use SCREAMING_SNAKE_CASE naming convention

Files:

  • lib/window/ipcEvents.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: run-tests / test
  • GitHub Check: native-build-check / build-check-windows
  • GitHub Check: native-build-check / build-check-mac
🔇 Additional comments (1)
lib/window/ipcEvents.ts (1)

632-643: LGTM! Error message updated for self-hosting.

The error message change from "Local server not running" to "Server not reachable" is appropriate for the self-hosting use case where the server may be remote.

Comment on lines +52 to +58
- name: Set up environment for self-hosted
run: |
echo "VITE_GRPC_BASE_URL=\"${{ inputs.server_url }}\"" >> .env
echo "VITE_ITO_ENV=\"dev\"" >> .env
echo "ITO_ENV=\"dev\"" >> .env
echo "CSC_IDENTITY_AUTO_DISCOVERY=false" >> .env
cat .env
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, explore repository structure to understand build setup
find . -maxdepth 2 -type f \( -name "vite.config.*" -o -name "electron-builder.config.*" -o -name "package.json" -o -name ".env*" \) | head -20

Repository: heyito/ito

Length of output: 156


🏁 Script executed:

# Search for .env parsing and VITE_GRPC_BASE_URL usage
rg -n "VITE_GRPC_BASE_URL" -A 2 -B 2

Repository: heyito/ito

Length of output: 2683


🏁 Script executed:

# Look for vite config to understand how env vars are loaded
fd -e "vite.config" -e "vite.config.js" -e "vite.config.ts"

Repository: heyito/ito

Length of output: 77


🏁 Script executed:

# Check for .env example files to see intended format
fd ".env" --type f

Repository: heyito/ito

Length of output: 67


🏁 Script executed:

# Search for how environment variables are parsed in the project
rg -n "process\.env\.|import\.meta\.env\." --type ts --type js | head -30

Repository: heyito/ito

Length of output: 2531


🏁 Script executed:

cat .env.example

Repository: heyito/ito

Length of output: 442


🏁 Script executed:

cat electron.vite.config.ts

Repository: heyito/ito

Length of output: 1807


🏁 Script executed:

cat electron-builder.config.js

Repository: heyito/ito

Length of output: 3294


🏁 Script executed:

# Check if there are any comments or docs about .env format in the repo
rg -n "\.env" --type md | head -20

Repository: heyito/ito

Length of output: 1049


🏁 Script executed:

cat -n .github/workflows/selfhosted-build.yml | sed -n '50,120p'

Repository: heyito/ito

Length of output: 2818


🏁 Script executed:

# Search for how CSC_IDENTITY_AUTO_DISCOVERY is used in electron-builder
rg -n "CSC_IDENTITY_AUTO_DISCOVERY" -A 2 -B 2

Repository: heyito/ito

Length of output: 1010


Remove CSC_IDENTITY_AUTO_DISCOVERY from .env file—it's already set as an environment variable.

The CSC_IDENTITY_AUTO_DISCOVERY=false setting is written to .env (line 57) but is also set directly as a shell environment variable in the electron-builder step (line 65). Since electron-builder reads this from the shell environment, the .env entry is redundant. Set it only via the env: directive in the build step to avoid duplication.

The quoted environment variables (e.g., VITE_GRPC_BASE_URL="...") are correctly formatted—Vite's dotenv parser automatically strips surrounding quotes.

🤖 Prompt for AI Agents
In .github/workflows/selfhosted-build.yml around lines 52 to 58, the workflow
currently appends CSC_IDENTITY_AUTO_DISCOVERY=false into the .env file even
though that variable is already provided via the shell environment for the
electron-builder step; remove the line that echoes CSC_IDENTITY_AUTO_DISCOVERY
into .env so the variable is only set via the build step's env: directive and
avoid duplication, and verify no other workflow steps write the same variable
into .env.

Comment on lines +283 to +284
docker exec -it ito-server node ./node_modules/node-pg-migrate/bin/node-pg-migrate.js up
```
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Check if the migration script exists at the documented path
fd -t f "node-pg-migrate.js" server/

Repository: heyito/ito

Length of output: 36


🏁 Script executed:

# Also check the overall directory structure and package.json
cat -n package.json | head -50

Repository: heyito/ito

Length of output: 2673


🏁 Script executed:

# Look for node-pg-migrate in node_modules or check if it's installed
fd -t d "node-pg-migrate" . --max-depth 3

Repository: heyito/ito

Length of output: 36


🏁 Script executed:

# Check the README around the context to understand the migration setup better
sed -n '270,300p' README.md

Repository: heyito/ito

Length of output: 958


🏁 Script executed:

# Search for node-pg-migrate in the entire repository
rg "node-pg-migrate" .

Repository: heyito/ito

Length of output: 5064


🏁 Script executed:

# Check if there's a server package.json
fd -t f "package.json" server/

Repository: heyito/ito

Length of output: 99


🏁 Script executed:

# Check Docker-related files for dependency installation
fd -t f "Dockerfile" . --max-depth 2

Repository: heyito/ito

Length of output: 73


🏁 Script executed:

# Check docker-compose configuration
fd -t f "docker-compose.yml" . --max-depth 2

Repository: heyito/ito

Length of output: 81


🏁 Script executed:

# Check Dockerfile for dependency installation
cat server/Dockerfile

Repository: heyito/ito

Length of output: 569


🏁 Script executed:

# Check the server-level npm scripts for migration commands
sed -n '20,50p' server/package.json

Repository: heyito/ito

Length of output: 1577


Add missing migrations directory argument to the Docker exec command.

The Docker migration command should include the --migrations-dir flag to specify the correct migrations directory. The actual migration script in server/scripts/migrate.sh uses --migrations-dir src/migrations, but the README command omits this argument. Update the command to:

docker exec -it ito-server node ./node_modules/node-pg-migrate/bin/node-pg-migrate.js --migrations-dir src/migrations up
🤖 Prompt for AI Agents
In README.md around lines 283 to 284, the docker exec command for running
migrations omits the --migrations-dir argument; update the command to include
the migrations directory used by the project's migration script (use
--migrations-dir src/migrations) so the Docker command mirrors
server/scripts/migrate.sh and points node-pg-migrate at the correct migrations
folder.

@NotGovernor
Copy link
Contributor

NotGovernor commented Dec 16, 2025

Heartbreaking that you are having to shut down. This is a hugely important tool. Thank you for your work on it, and for open sourcing it going forward with self-hosting options.
♥💛🖤

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants