Skip to content
This repository was archived by the owner on Jan 13, 2026. It is now read-only.
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
138 changes: 138 additions & 0 deletions .github/workflows/selfhosted-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
name: Build Self-Hosted

on:
workflow_dispatch:
inputs:
server_url:
description: 'Your server URL (e.g., http://myserver:3001)'
required: true
type: string
build_mac_arm64:
description: 'Build for macOS (Apple Silicon)'
default: true
type: boolean
build_mac_x64:
description: 'Build for macOS (Intel)'
default: false
type: boolean

permissions:
contents: read

jobs:
build-mac-arm64:
if: ${{ inputs.build_mac_arm64 }}
runs-on: macos-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Bun
uses: oven-sh/setup-bun@v1

- name: Install dependencies
run: bun install

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

- 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 ../..
done

- 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
Comment on lines +52 to +58
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.


- name: Build Electron app
run: bun run electron-vite build

- name: Package macOS DMG (arm64)
env:
CSC_IDENTITY_AUTO_DISCOVERY: false
run: |
bunx electron-builder --config electron-builder.config.js --mac dmg --arm64 --publish=never

- 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
ls -la dist/*.dmg

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

build-mac-x64:
if: ${{ inputs.build_mac_x64 }}
runs-on: macos-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Bun
uses: oven-sh/setup-bun@v1

- name: Install dependencies
run: bun install

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

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

- 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

- name: Build Electron app
run: bun run electron-vite build

- name: Package macOS DMG (x64)
env:
CSC_IDENTITY_AUTO_DISCOVERY: false
run: |
bunx electron-builder --config electron-builder.config.js --mac dmg --x64 --publish=never

- name: Rename artifact with server info
run: |
SERVER_HOST=$(echo "${{ inputs.server_url }}" | sed 's|.*://||' | sed 's|:.*||' | sed 's|/.*||')
mv dist/Ito-Installer.dmg "dist/Ito-selfhosted-${SERVER_HOST}-x64.dmg" || true
ls -la dist/*.dmg

- name: Upload macOS DMG (x64)
uses: actions/upload-artifact@v4
with:
name: Ito-macOS-x64
path: dist/*.dmg
99 changes: 99 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,105 @@ bun run lint:fix # Fix linting issues

---

## 🏠 Self-Hosting

This fork includes support for self-hosting Ito with a remote server. Instead of using the managed cloud service, you can run your own server and connect to it from anywhere.

### Server Setup

1. **Clone and configure the server**:
```bash
git clone https://github.com/evoleinik/ito.git
cd ito/server
cp .env.example .env
```

2. **Edit `.env`** with your settings:
```bash
PORT=3001 # Use 3001 if 3000 is in use
DB_HOST=db
DB_USER=ito
DB_PASS=your-secure-password
GROQ_API_KEY=your-groq-api-key # Get from console.groq.com
S3_ENDPOINT=http://minio:9000
REQUIRE_AUTH=false # For personal use
```

3. **Start the server** (requires Docker):
```bash
docker compose up -d --build
```

4. **Run database migrations**:
```bash
docker exec -it ito-server node ./node_modules/node-pg-migrate/bin/node-pg-migrate.js up
```
Comment on lines +283 to +284
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.


5. **Verify the server is running**:
```bash
curl http://localhost:3001
# Should return: "Welcome to the Ito Connect RPC server!"
```

### Building the Client (GitHub Actions)

The easiest way to build your own client is using GitHub Actions:

1. **Fork this repository** to your GitHub account
2. Go to **Actions** → **Build Self-Hosted**
3. Click **Run workflow**
4. Enter your server URL (e.g., `http://myserver:3001`)
5. Select your platform (macOS arm64 or x64)
6. Wait for the build to complete (~5-10 minutes)
7. Download the DMG from **Artifacts**

### Building the Client (Local)

Alternatively, build locally with your server URL:

```bash
# 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

# Create DMG (macOS, arm64 only to avoid electron-builder issues)
bunx electron-builder --config electron-builder.config.js --mac dmg --arm64 --publish=never
```

### Installing the Client

1. Mount the DMG from `dist/Ito-Installer.dmg`
2. Drag `Ito.app` to Applications
3. **Re-sign the app** (required for ad-hoc signed builds):
```bash
xattr -cr /Applications/Ito.app
codesign --force --deep --sign - /Applications/Ito.app
```
4. Launch the app

### Network Setup

For remote access, you can use:
- **Tailscale**: Recommended for secure, zero-config networking. Install on both client and server machines, then use the Tailscale hostname (e.g., `http://box:3001`)
- **VPN**: Any VPN that gives you direct network access to your server
- **Port forwarding**: Expose port 3001 (not recommended for security reasons)

### Server Auto-Start

The Docker services are configured with `restart: always`, so they'll automatically start on boot. Ensure Docker is enabled:

```bash
# Linux
sudo systemctl enable docker

# Verify services are running
docker compose ps
```

---

## 🏗️ Architecture

### Client Architecture
Expand Down
14 changes: 7 additions & 7 deletions lib/window/ipcEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -605,12 +605,12 @@ export function registerIPC() {
// Server health check
handleIPC('check-server-health', async () => {
try {
const response = await fetch(
`http://localhost:${import.meta.env.VITE_LOCAL_SERVER_PORT}`,
{
method: 'GET',
},
)
const serverUrl =
import.meta.env.VITE_GRPC_BASE_URL ||
`http://localhost:${import.meta.env.VITE_LOCAL_SERVER_PORT || '3000'}`
const response = await fetch(serverUrl, {
method: 'GET',
})

if (response.ok) {
const text = await response.text()
Expand All @@ -634,7 +634,7 @@ export function registerIPC() {
? 'Connection timed out'
: error.message?.includes('ECONNREFUSED') ||
error.message?.includes('fetch')
? 'Local server not running'
? 'Server not reachable'
: error.message || 'Unknown error occurred'

return {
Expand Down