5.8 KiB
Toes - Claude Code Guide
What It Is
Personal web appliance that auto-discovers and runs multiple web apps on your home network.
"Plug it in, turn it on, and forget about the cloud."
How It Works
- Host server scans
/appsdirectory for valid apps - Valid app = has
package.jsonwithscripts.toesentry - Each app spawned as child process with unique port (3001+)
- Dashboard UI shows all apps with current status, logs, and links
Key Files
Server (src/server/)
apps.ts- The heart: app discovery, process management, health checks, auto-restartapi/apps.ts- REST API for app lifecycle (start/stop/restart, logs, icons, rename)api/sync.ts- File sync protocol (manifest, push/pull, watch)index.tsx- Entry point (minimal, initializes Hype)shell.tsx- HTML shell for web UI
Client (src/client/)
components/- Dashboard, Sidebar, AppDetail, Navmodals/- NewApp, RenameApp, DeleteApp dialogsstyles/- Forge CSS-in-JS (themes, buttons, forms, layout)state.ts- Client state managementapi.ts- API client
CLI (src/cli/)
commands/manage.ts- list, start, stop, restart, info, new, rename, delete, opencommands/sync.ts- push, pull, synccommands/logs.ts- log viewing with tail support
Shared (src/shared/)
- Code shared between frontend (browser) and backend (server)
types.ts- App, AppState, Manifest interfaces- IMPORTANT: Cannot use filesystem or Node APIs (runs in browser)
Lib (src/lib/)
- Code shared between CLI and server (server-side only)
templates.ts- Template generation for new apps- Can use filesystem and Node APIs (never runs in browser)
Other
apps/*/package.json- Must have"toes": "bun run --watch index.tsx"scriptTODO.txt- Task list
Tools
Tools are special apps that appear as tabs in the dashboard rather than standalone entries in the sidebar. They integrate directly into the Toes UI and can interact with the currently selected app.
Creating a Tool
Add toes.tool to your app's package.json:
{
"toes": {
"icon": "🔧",
"tool": true
},
"scripts": {
"toes": "bun run --watch index.tsx"
}
}
How Tools Work
- Tools run as regular apps (spawned process with unique port)
- Displayed as iframes overlaying the tab content area
- Receive
?app=<name>query parameter for the currently selected app - Iframes are cached per tool+app combination (never recreated once loaded)
- Tool state persists across tab switches
- App paths: When accessing app files, tools must use
APPS_DIR/<app>/current(not justAPPS_DIR/<app>) to resolve through the version symlink
CLI Flags
toes list # Lists all apps including tools
toes list --tools # Lists tools only
toes list --apps # Lists regular apps only (excludes tools)
Tool vs App
| Aspect | Regular App | Tool |
|---|---|---|
toes.tool |
absent/false | true |
| UI location | Sidebar | Tab bar |
| Rendering | New browser tab | Iframe in dashboard |
| Context | Standalone | Knows selected app via query param |
Tech Stack
- Bun runtime (not Node)
- Hype (custom HTTP framework wrapping Hono) from git+https://git.nose.space/defunkt/hype
- Forge (typed CSS-in-JS) from git+https://git.nose.space/defunkt/forge
- Commander + kleur for CLI
- TypeScript + Hono JSX
Running
bun run --hot src/server/index.tsx # Dev mode with hot reload
App Structure
// apps/example/index.tsx
import { Hype } from "@because/hype"
const app = new Hype()
app.get("/", (c) => c.html(<h1>Content</h1>))
export default app.defaults
Conventions
- Apps get
PORTenv var from host - Each app is isolated process with own dependencies
- No path-based routing - apps run on separate ports
DATA_DIRenv controls where apps are discovered- Path aliases:
$→ server,@→ shared,%→ lib
Environment Variables
Env vars are stored per-app in TOES_DIR/env/:
${DATA_DIR}/toes/env/
clock.env # env vars for clock app
todo.env # env vars for todo app
TOES_DIR defaults to ${DATA_DIR}/toes. Apps cannot access this directory directly.
Current State
Infrastructure (Complete)
- App discovery, spawn, watch, auto-restart with exponential backoff
- Health checks every 30s (3 failures trigger restart)
- Port pool (3001-3100), sticky allocation per app
- SSE streams for real-time app state and log updates
- File sync protocol with hash-based manifests
CLI
- Full management:
toes list|start|stop|restart|info|new|rename|delete|open - File sync:
toes push|pull|sync - Logs:
toes logs [-f] <app>
Check TODO.txt for planned features
Coding Guidelines
TS files should be organized in the following way:
- imports
- re-exports
- const/lets
- enums
- interfaces
- types
- classes
- functions
- module init (top level function calls)
In each section, put the exports first, in alphabetical order.
Then, after the exports (if there were any), put everything else,
also in alphabetical order.
For single-line functions, use const fn = () => {} and put them in the
"functions" section of the file.
All other functions use the function blah(){} format.
Example:
import { code } from "coders"
import { something } from "somewhere"
export type { SomeType }
const RETRY_TIMES = 5
const WIDTH = 480
enum State {
Stopped,
Starting,
Running,
}
interface Config {
name: string
port: number
}
type Handler = (req: Request) => Response
class App {
config: Config
constructor(config: Config) {
this.config = config
}
}
const isApp = (name: string) =>
apps.has(name)
function createApp(name: string): App {
const app = new App({ name, port: 3000 })
apps.set(name, app)
return app
}
function start(app: App): void {
console.log(`Starting ${app.config.name}`)
}