Open-source, self-hosted coding-agent workspace. Each workspace is a real cloned repo. pi is the engine, with pi-codex-goal as a first-class citizen.
Mobile-first. Small-team ready. macOS + iOS clients planned.
Run Waynode on infrastructure you control. Your repositories, database, credentials, provider accounts, and billing stay with you. Self-hosted installs do not enable Waynode hosted billing, usage limits, or payment collection.
Waynode Cloud runs the same open-source workspace for your team, with managed updates, isolated workspaces, backups, and support. New cloud organizations get a 15-day trial; choose Starter, Pro, or Team only when you are ready to keep using the service. Web subscriptions use Stripe. Native App Store billing is intentionally not enabled until its server-verified entitlement flow is shipped; never enter Stripe credentials into a native client.
Operators: hosted billing is deliberately disabled unless
WAYNODE_DEPLOYMENT=hostedis set alongside the Stripe configuration. Do not set that flag on a self-host deployment.
git clone https://github.com/fornace/waynode.git
cd waynode
cp .env.example .env
# Edit .env — set SESSION_SECRET, ENCRYPTION_KEY, and OAuth credentials
docker compose up -d
# → http://localhost:3000For a production self-host, set a public APP_URL, use HTTPS at your reverse
proxy, keep SESSION_SECRET and ENCRYPTION_KEY in a secret manager, and
configure GitHub and/or GitLab OAuth callbacks for that exact URL. Never commit
your .env, OAuth client secrets, or provider access tokens.
# Terminal 1: backend
npm install
cp .env.example .env
npm run dev
# Terminal 2: frontend (hot reload)
cd frontend
npm install
npm run devTo push to this repo you need a GitHub token with the workflow scope
(it is separate from repo, and is required to push any change that touches
.github/workflows/). Without it, git push will be rejected with:
refusing to allow an OAuth App to create or update workflow … without workflow scope.
gh auth login # first time
gh auth refresh -h github.com -s workflow # add the workflow scopeEnd-user login via GitHub OAuth (see OAuth Setup below) only needs
read:user / repo-type scopes — pushing workflows is a developer action,
so end-user tokens never need the workflow scope.
- Go to GitHub → Settings → Developer settings → OAuth Apps → New OAuth App
- Set Authorization callback URL to
https://your-domain/auth/github/callback - Copy the Client ID and generate a Client Secret
- Put them in
.envasGITHUB_CLIENT_ID/GITHUB_CLIENT_SECRET
- Go to GitLab → User Settings → Applications → New Application
- Set Redirect URI to
https://your-domain/auth/gitlab/callback - Scopes:
read_user,read_api,read_repository - Put them in
.envasGITLAB_CLIENT_ID/GITLAB_CLIENT_SECRET
Pushes to main trigger .github/workflows/deploy.yml, which rsyncs the
source to the server and rebuilds the Docker container (docker compose build --no-cache). The live site is https://waynode.fornace.net.
It requires three GitHub Actions secrets (repo → Settings → Secrets and variables → Actions):
| Secret | Value |
|---|---|
DEPLOY_HOST |
The host currently serving waynode.fornace.net |
DEPLOY_USER |
SSH user (e.g. root) |
DEPLOY_SSH_KEY |
Private SSH key authorized on the server |
Set them once with the CLI:
gh secret set DEPLOY_HOST --body '<serving-host>'
gh secret set DEPLOY_USER --body 'root'
gh secret set DEPLOY_SSH_KEY < ~/.ssh/waynode_deploy_keyThe Dockerfile is a multi-stage build: it compiles the frontend
(npm run build) then installs pi + the pi-codex-goal / pi-lean-ctx
plugins and configures the fornace LLM provider, so frontend/dist does not
need to be committed.
Before treating a deployment as successful, confirm the container on
DEPLOY_HOST was actually replaced (not merely that the public domain answers
HTTP). The compose file used by CI and the compose file running on the host
must be the same topology. For Waynode Cloud, configure the hosted-only Stripe
variables in the host's root-owned .env (mode 0600), then verify
/api/billing/enabled returns {"enabled":true} and complete a signed-webhook
and isolated authenticated E2E run. See pricing operations.
Use the hosted launch gate for the complete
reconciliation and payment-verification checklist.
┌─────────────────────────────────────────────────┐
│ Clients: Web (React) │ macOS (Tauri) │ iOS │
├─────────────────────────────────────────────────┤
│ Express server │
│ • GitHub + GitLab OAuth │
│ • Space = cloned repo on disk │
│ • Session = pi session (JSONL-backed) │
│ • Persistent `pi --mode rpc` agents, streamed over SSE │
│ • Agent lives in server, survives client navigation │
│ • SQLite: secrets, metadata, session index │
│ • Terminal: node-pty → WebSocket → xterm.js │
├─────────────────────────────────────────────────┤
│ pi runtime (per-space working directory) │
│ • pi-codex-goal preinstalled │
│ • AGENTS.md + skills/ per space │
└─────────────────────────────────────────────────┘
| Concept | Description |
|---|---|
| Space | A cloned git repository. Each space has its own AGENTS.md, skills, and sessions. |
| Session | A conversation within a space. Backed by pi's JSONL session format. |
| Chat tab | Conversation via a persistent pi --mode rpc agent, token-streamed over SSE. Survives navigation. |
| Terminal tab | Full pi TUI via node-pty → WebSocket → xterm.js. For /goal, model switching, raw power. |
| Goal mode | Send-as-Goal wraps your prompt to instruct pi to use create_goal and run autonomously. |
| Secrets | AES-256 encrypted, stored in SQLite. Scoped globally or per-space. Injected as env vars to pi. |
MIT