Forge is an Electron-like desktop application framework using Rust and Deno. Build cross-platform desktop apps with TypeScript/JavaScript while leveraging native system capabilities through a secure, capability-based API.
Before getting started, ensure you have:
- Deno 1.40 or later (deno.land)
- A code editor (VS Code recommended)
curl -fsSL https://forge-deno.com/install.sh | shThis installs both forge and forge-runtime to ~/.forge/bin/.
Download the latest release for your platform from GitHub Releases and extract to ~/.forge/bin/:
# Linux
tar -xzf forge-x86_64-unknown-linux-gnu.tar.gz -C ~/.forge/bin/
# macOS
tar -xzf forge-aarch64-apple-darwin.tar.gz -C ~/.forge/bin/
# Add to PATH (add to ~/.bashrc or ~/.zshrc)
export PATH="$HOME/.forge/bin:$PATH"If you're contributing to Forge itself, you'll need Rust 1.70+:
git clone https://github.com/LayerDynamics/forge.git
cd forge
cargo build --workspace --release
cargo install --path crates/forge_cli
cargo install --path crates/forge-runtimeCopy an example to start a new project:
# Copy the minimal example
cp -r examples/example-deno-app my-app
cd my-app
# Or use a framework example
cp -r examples/react-app my-app # React with TypeScript
cp -r examples/nextjs-app my-app # Next.js-style patterns
cp -r examples/svelte-app my-app # Svelte with TypeScriptThis creates a new Forge application with the following structure:
my-app/
├── manifest.app.toml # App configuration and capabilities
├── deno.json # Deno configuration
├── src/
│ └── main.ts # Main Deno entry point
└── web/
└── index.html # UI entry point
The manifest defines your app's metadata and capabilities:
[app]
name = "My App"
identifier = "com.example.myapp"
version = "0.1.0"
[windows]
width = 800
height = 600
resizable = true
# Capability declarations (optional)
[capabilities.fs]
read = ["~/.myapp/*"]
write = ["~/.myapp/*"]
[capabilities.channels]
allowed = ["*"]The main Deno entry point handles app logic:
import { openWindow, windowEvents } from "runtime:ui";
// Open the main window
const win = await openWindow({
url: "app://index.html",
width: 800,
height: 600,
title: "My App"
});
// Listen for events from the renderer
for await (const event of windowEvents()) {
console.log("Event:", event.channel, event.payload);
}The UI is standard HTML/CSS/JS served via the app:// protocol:
<!DOCTYPE html>
<html>
<head>
<title>My App</title>
</head>
<body>
<h1>Hello, Forge!</h1>
<script>
// Communicate with Deno backend
window.host.send("hello", { message: "Hi from renderer!" });
// Listen for messages from backend
window.host.on("reply", (data) => {
console.log("Received:", data);
});
// Signal ready
window.host.emit("ready");
</script>
</body>
</html>Run your app in development mode with hot reload:
forge dev my-appThis starts the Forge runtime with:
- Live reload on file changes
- Development-friendly CSP settings
- Console output in terminal
Forge provides native capabilities through runtime:* modules:
import { openWindow, dialog, createTray } from "runtime:ui";
// Open a window
const win = await openWindow({ url: "app://index.html" });
// Show a dialog
const path = await dialog.open({ title: "Select File" });
// Create a tray icon
const tray = await createTray({ tooltip: "My App" });import { readTextFile, writeTextFile, watch } from "runtime:fs";
// Read a file
const content = await readTextFile("./config.json");
// Write a file
await writeTextFile("./output.txt", "Hello!");
// Watch for changes
const watcher = await watch("./src");
for await (const event of watcher) {
console.log("File changed:", event.paths);
}import { fetchJson } from "runtime:net";
const data = await fetchJson("https://api.example.com/data");import { clipboard, notify, info } from "runtime:sys";
// System info
const sysInfo = info();
console.log(sysInfo.os, sysInfo.arch);
// Clipboard
await clipboard.write("Hello");
const text = await clipboard.read();
// Notifications
await notify("Title", "Body text");import { spawn } from "runtime:process";
const proc = await spawn("ls", { args: ["-la"] });
for await (const line of proc.stdout) {
console.log(line);
}
await proc.wait();Forge uses a simple message-passing model for communication between Deno and the renderer:
// In web/index.html
window.host.send("channel-name", { data: "value" });// In src/main.ts
win.send("channel-name", { data: "value" });// In Deno - listen for all window events
for await (const event of windowEvents()) {
if (event.channel === "user-action") {
// Handle event
}
}
// In renderer - listen for specific channel
window.host.on("update", (data) => {
// Handle update
});Build your app for distribution:
# Build the app bundle
forge build my-app
# Create platform-specific packages
forge bundle my-app
# Sign the bundle (macOS/Windows)
forge sign my-app/bundle/MyApp.dmg --identity "Developer ID"Your app needs an icon for bundling. Create one before release:
# Create a placeholder icon
forge icon create my-app/assets/icon.png
# Validate your icon
forge icon validate my-appIcon requirements: 1024x1024 PNG with transparency. See the Icons Guide for details.
Code signing is required for macOS distribution and recommended for Windows. Configure signing in your manifest:
[bundle.macos]
sign = true
signing_identity = "Developer ID Application: Your Name (TEAMID)"See the Code Signing Guide for platform-specific instructions.
Check out the example apps in the examples/ directory:
- example-deno-app - Minimal starter app
- react-app - React with TypeScript and IPC demo
- nextjs-app - Next.js-style routing patterns
- svelte-app - Svelte with TypeScript and todo list
- todo-app - File persistence, menus, IPC patterns
- text-editor - Full file operations, dialogs, context menus
- weather-app - HTTP fetch, notifications, tray icons
- system-monitor - System info, multi-window, process management
- Read the Architecture Overview
- Explore the API Reference
- Check the Example Apps
- GitHub Issues: github.com/LayerDynamics/forge/issues
- Documentation: forge-deno.com/docs