I built a self-hosted workspace to run AI coding CLIs side by side (MIT, OpenCode first)
Hey r/selfhosted. I built this, so read it with that bias in mind.
SpaceApp is a self-hosted web workspace for running AI coding CLIs side by side. Each CLI runs in its own pane, panes live in rooms, and the sessions persist, so a room is still there when you come back to it.
Why I built it
I was running coding agents in a pile of terminal windows and tmux splits, each with its own login and its own idea of which folder it was in. I wanted one place on my own machine where they all run, where I can see which one is waiting for me, and where nothing depends on somebody else's SaaS. It became the tool I work in all day.
What a public install does today
- 11 AI coding CLIs in the public installer, OpenCode first: OpenAI Codex, Claude Code, Gemini CLI, Qwen Code, Kimi Code, Grok Build, DeepSeek CLI (experimental), Cursor, GitHub Copilot and Autohand. Installable is not the same as tested: the public install rehearsal ran OpenCode end to end. Claude Code has its own install flow that you start yourself.
- Up to 16 panes per room.
- Your own AI accounts. Providers connect through their official login flows or a masked credential field.
- The containers get no bind mount of your host filesystem by default. A folder shows up only after you register it as a workspace, and read-only mounts are supported.
- Portable, checksummed backups of the app data, PostgreSQL and owner memory. Provider credentials, login state, workspace contents and browser profiles stay out of them.
- Telemetry is opt-in, and No is the default.
- MIT-licensed.
What it is not
- Rooms organize sessions; they are not a security boundary. Container separation is not a sandbox against a hostile workload.
- It is not lightweight. The installer wants at least 4 CPUs, 8 GB RAM and 15 GiB of free disk. The standard profile, which adds a managed Chromium container, recommends 8 CPUs, 16 GB RAM and 25 GiB.
- It listens only on its own host by default. A phone, a tablet or another computer can reach it once you make the install reachable yourself.
- There are optional modules (copilots and studios) that a new install starts without. I'm leaving them out of this post: it is about the CLI workspace.
How to try it
Node.js 20.11 or newer, then:
npx --yes run-spaceapp@latest install
It runs on Linux, macOS and Windows 11. There's no compose file to copy: the launcher manages the containers itself (core API and web app, CLI runtimes, PostgreSQL, Temporal). The default light profile has no browser container; the standard profile adds it. After the readiness checks, a one-time setup token creates the first account, and you register the folders you want the agents to see.
If you'd rather click around first, there is a live demo with invented data: spaceapp.dev
Code: oll4com/spaceapp
What I'd like to hear from you
- Which CLIs you run together, and what a room should give you that a tmux session doesn't.
- Where the install breaks on your hardware or OS.
- Whether the backup split (what goes in, what stays out) matches what you would expect from a self-hosted tool.
I'll be in the comments.
2 comments
Logins stay out of its context: the Vault fills a login or a one-time code in the pane's browser, and Nous gets back a status, not the value. Self-hosted and MIT-licensed: oll4com/spaceapp
Show HN: SpaceApp – Run several AI coding CLIs side by side, self-hosted
1 point
One tool layer for eleven AI coding CLIs: what we learned building SpaceApp
Why I built SpaceApp, in my own words:
> I built it to be my main work tool. I'm a developer! And it became my main tool and work environment. I no longer need to open email, social media or any development program: absolutely nothing. I'm inside SpaceApp all day; I even watch YouTube videos and listen to music through SpaceApp.
SpaceApp is a self-hosted workspace where AI coding CLIs (Claude Code, Codex, OpenCode, Gemini CLI and others) run side by side in persistent rooms. Each of them comes with its own config format, its own way of registering MCP servers, its own sign-in, its own "resume the last session" flag and its own idea of what Esc means. This post is about the two parts that turned out to matter most: where the agents' tools come from, and where the agents' sessions live.
The N×M problem
Every CLI wants its tools declared in its own file: one JSON shape for one, a TOML table for another, a command-line flag for a third. Multiply that by the tools you want every agent to have (a managed browser, shared memory, tasks, files, deployment checks) and you get N×M configurations that drift apart. Worse, a CLI usually starts every declared MCP server when the session starts, whether or not the agent ever calls it. Twelve panes × five servers is a lot of idle processes.
Capability first, MCP on demand
The first rule we settled on: use what the runtime already has. If the CLI has a shell, file editing and image input, the agent uses them directly. A tool layer that wraps cat in an MCP call only adds latency and tokens.
For SpaceApp's own operations (status, logs, the managed browser, memory), the route depends on the runtime:
- Codex and Claude Code panes register no MCP server at startup. When the agent needs a SpaceApp operation it calls one on-demand entrypoint, space-capability. It answers "what can I call and with which arguments" from cached, compact schemas without starting anything. The first real call starts a small worker on a pane-private Unix socket, and that worker starts only the backend that was asked for: at most three per pane, each stopped after an idle timeout.
- Other runtimes keep a single dispatcher (space-mcp-dispatcher) declared once from a shared registry. The dispatcher also spawns registry servers only when called and stops them when idle.
Two details turned out to matter more than the transport:
1. A declared tool grants nothing. Every operation still passes its own schema, role, approval and broker gates, whichever path it came through. MCP is a transport, not an authorization model.
2. No replays after ambiguity. If a call may have executed and the transport broke, the call is not retried automatically. A duplicated deploy check is annoying; a duplicated "send" is worse.
The operator can override routing per runtime, model and tool (Automatic, Native only, Allow fallback, Disabled), and the settings page explains which route a call will take.
This path is still marked experimental in the architecture doc: it is enabled for Codex and Claude Code only, because each runtime's capabilities have to be verified before it gets the on-demand path.
Sessions that outlive the web server
The second lesson: the process that serves the UI must not own the agents.
In SpaceApp each CLI runs in a terminal session owned by a separate pane host process, not by the API. The API attaches to that session over a socket. When the API restarts during a deploy, or the browser drops its connection, the agent keeps working, and the pane re-attaches to the same session. The pane host tracks session generations and output sequence numbers, so a reconnecting view knows where it left off.
That separation is also why we never resume a provider conversation by copying a transcript. If a CLI has a native resume (a session id, a thread id), we use it; if it does not, we say so instead of pretending.
Eleven TUIs are eleven TUIs
Things that broke along the way, in roughly the order they hurt:
- Stop means different keys. Esc interrupts a turn in some CLIs, Ctrl-C in others, and one is stopped through its own server API. Stop now sends each TUI its own interrupt.
- Model pickers are UI, not API. Switching a model in one CLI means driving its /model menu. In a short pane the menu shows a scrolled window of rows and clips its hints, so the picker has to read scroll markers and page through the menu. It also has to pick "this session only", never the saved default every other session shares.
- "Ready" screens lie. An old, partial MCP startup message once kept a newer, ready Codex screen from being detected. Now a newer header supersedes the earlier partial marker, while a screen that is still loading keeps blocking.
- Contracts are not end-to-end proof. A contract suite checks every declared start, resume and login command line of every declared runtime against the wrapper filter. That catches regressions in our adapters; it does not prove that the next upstream release of a CLI behaves the same. We track that gap openly.
What it does not solve
- Isolation between users. Rooms organise work; they are not a security boundary between mutually untrusted users. The Unix user is the outer trust boundary, so one install should serve one trust domain.
- Your provider's view. Self-hosting keeps the workspace and its data on your host. The model providers you configure still receive what the agents send them.
Try it
SpaceApp is open source (MIT) and installs with:
npx --yes run-spaceapp@latest install
Architecture: github.com/oll4com/spaceapp/blob/main/docs/archit… · Known limitations: docs/quality/KNOWN_LIMITATIONS.md in the repository.
If you run several coding agents on one codebase, I would like to hear how you keep their tools and sessions in order.
Six AI models played chess through their CLIs. None of them delivered checkmate.
I build SpaceApp, a self-hosted workspace for running AI coding CLIs side by side. On 18 October I let six models play chess against each other there, each through its own CLI. Nobody won by checkmate. The way they lost is the interesting part.
The setup
- Players: Opus 5.5 and Fable 5.1 (through Claude Code), GPT-6-Astra and GPT-6.1-Sol (through Codex), all at xhigh reasoning; Gemini 4 Argon (High) and Gemini 3.8 Flash (High) (through the Gemini CLI).
- Format: a 4-round Swiss tournament, 12 games.
- Clock: 120 seconds per move. Each move is one one-shot turn of the model's CLI: it gets the position and answers with a move.
- Strikes: a timeout, a provider error or a second illegal answer plays a random legal move for that side and counts a strike. The third strike forfeits the game.
- Length: after 60 plies (30 moves each) the game is adjudicated on material.
The results
| # | Model | Points | W / D / L | Elo | Avg per move | Forfeits |
|---|---|---|---|---|---|---|
| 1 | Opus 5.5 (xhigh) | 3.5 | 3 / 1 / 0 | 1535 | 13 s | 0 |
| 2 | Gemini 4 Argon (High) | 3.5 | 3 / 1 / 0 | 1535 | 23 s | 0 |
| 3 | GPT-6-Astra (xhigh) | 2 | 2 / 0 / 2 | 1500 | 37 s | 2 |
| 4 | Fable 5.1 (xhigh) | 1 | 1 / 0 / 3 | 1478 | 26 s | 3 |
| 5 | Gemini 3.8 Flash (High) | 1 | 1 / 0 / 3 | 1477 | 40 s | 3 |
| 6 | GPT-6.1-Sol (xhigh) | 1 | 1 / 0 / 3 | 1475 | 34 s | 3 |
- Zero checkmates. 11 of the 12 games ended on a third strike.
- The 12th game, Gemini 4 Argon against Opus 5.5, ran the full 60 plies and was drawn on material.
- Opus 5.5 and Gemini 4 Argon were the only models that never needed a fallback move. Opus 5.5 was also the fastest, at 13 seconds per move on average.
What it measures, and what it does not
Twelve games are a small sample, and a tournament decided by forfeits says very little about chess strength. What it does show is how reliably a model answers through its CLI when every answer has a deadline: no timeouts, no provider errors, no illegal moves, game after game. That is the property I care about most when agents run unattended for hours, and it is not the same ranking you get from a single-prompt leaderboard.
Try SpaceApp
SpaceApp runs on Linux, macOS and Windows 11 with Node.js 20.11 or newer:
npx --yes run-spaceapp@latest install
It is source-available under the Business Source License 1.1. Code, docs and known limitations: oll4com/spaceapp
If you have run something similar, I would like to know which models held up for you, and which rules you would change for a fairer second run.
1 comment
6 AI models played a 4-round Swiss chess tournament, each through its own CLI in SpaceApp. 120 s per move; three missed moves lose the game.
Zero checkmates: 11 of 12 games ended in a forfeit. Only Opus and Gemini 4 Argon never missed a move.
oll4com/spaceapp t.co
24 views
I built SpaceApp: a self-hosted workspace that runs AI coding CLIs side by side in persistent rooms.
OpenCode, Codex, Claude Code, Gemini, Qwen, Kimi, Grok and DeepSeek, each in its own pane, on your own machine.
oll4com/spaceapp
2 likes · 73 views
From one command to a first page: what changed in SpaceApp's setup
I build SpaceApp, a self-hosted workspace for running AI coding CLIs in isolated rooms. A room organizes work; each pane in it is an independent CLI or app session. The runtimes on offer are OpenCode, Codex, Gemini, Qwen Code, Kimi Code, Grok Build and an experimental DeepSeek CLI; Claude Code has its own owner-initiated installation flow.
The latest setup releases were about one stretch of the experience: the time between running the installer and getting a first useful answer. Here is that path step by step, and what changed on it.
1. Install
You need Linux, macOS or Windows 11, Node.js 20.11 or newer, and at least 4 CPUs, 8 GB RAM and 15 GiB of free disk. The standard profile, which adds a managed browser, recommends 8 CPUs, 16 GB RAM and 25 GiB.
npx --yes run-spaceapp@latest install
The launcher manages the core API and web app, the CLI runtimes, PostgreSQL and Temporal. The default light profile leaves out the managed Chromium; the standard profile starts a browser container as well.
2. Create the owner account
Once the installation passes its readiness checks, you use a one-time setup token to create the first account, register the folders you want agents to work in, and connect providers through their official login flows or masked credential input.
Host folders are visible only after you register them as workspaces, and read-only mounts are supported.
New: the setup wizard's Ready step offers three first prompts you can copy.
3. The first model
The first OpenCode pane starts on a free model, Big Pickle. A model counts as working only after a real tool call: it has to read a small file with its read tool and answer with the random token inside it. When Big Pickle fails that check, the newest free tool-capable models are tried instead.
Free models can change upstream at any time. If the one you are on stops responding, pick another in OpenCode.
4. The first prompt
Two things used to get in the way here.
A room without a project had nowhere obvious to write. Now a CLI pane in such a room starts in /workspaces/<room>, so a first prompt like "build me a page" writes index.html there.
The first task could stop at a permission prompt. On macOS, a first OpenCode task could wait at "Access external directory /tmp" until someone answered. Now OpenCode's first run authorizes only its own private scratch directory, and the bootstrap instructions send temporary files there. Other paths outside the workspace still ask.
5. Staying current
The app now shows "Update available" when a newer release exists and opens the update command with a Copy button:
npx --yes run-spaceapp@latest update
What to know before you try it
- SpaceApp is designed for one trusted owner on one self-hosted instance.
- Backups are portable and checksummed and cover application data, PostgreSQL and owner memory. Provider credentials, login state, workspace contents and browser profiles are left out.
- Telemetry is opt-in. Answer No and nothing is sent. Answer Yes and one anonymous event is sent the first time an agent finishes an answer (event, random id, version, CPU architecture, date).
Feedback welcome
I would like to hear where the first five minutes still break for you: your OS, the profile the installer picked, and the step where it stopped. The full release notes are here: github.com/oll4com/spaceapp/releases
I Built BountyPilot: An Open-Weight AI That Helps Developers Find Coding Opportunities Worth Their Time
This is a submission for the Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle
What I Built
A GitHub issue with “$400” in its title looks like an opportunity. It still leaves the questions that determine whether I should spend a weekend on it: Is the work available? What counts as acceptance? Do I have the skills? Who can authorize payment?
I built BountyPilot around that gap between finding a lead and deciding it deserves my time.
It searches real public GitHub issues, retrieves their sources, and puts reward evidence beside the technical task. Local Gemma analysis adds a scope summary, relevant skills, estimated difficulty, suggestions, risks and unanswered questions. I can inspect the quoted sources, save an opportunity, and return to a persistent local pipeline.
The distinction I kept coming back to was a reward claim versus verified funding. An amount, a label or a maintainer-associated comment cannot establish that money is available or that my contribution would qualify. BountyPilot exposes that uncertainty instead of presenting a list of promised earnings.
!BountyPilot Discover showing real GitHub issues, filters and unverified reward evidence
The intended customer is an independent developer or freelancer who repeatedly evaluates paid coding leads. The practical value would be less time piecing together scattered threads and fewer decisions made from a bounty title alone.
Possible businesses around that workflow include managed setup and support, opt-in notifications, or paid hosting. Those are future possibilities, not implemented services or existing revenue. Hosted operation would need authentication, privacy boundaries and operating-cost work. I have no external-user or willingness-to-pay evidence yet.
These are genuine October 16 application screenshots. The video below was recorded October 17 using another real issue; screenshot amounts and timings belong to their original captures. Every pictured reward remains an unverified source claim.
Demo
{% embed player.mux.com %}
Watch the completed 88-second BountyPilot demo. The public player opens without sign-in. Select full screen for the interface text; the video is captioned and silent.
It shows real GitHub discovery, opening oll4com/spaceapp#60, reviewing source/payment evidence, selecting local Gemma 3 4B, generating an analysis, inspecting difficulty and citations, saving, and reloading Pipeline.
The recorded request performed new inference, measured at 14.537 seconds by the application. I removed 8.4 seconds of waiting and added reading holds; the edit discloses the shortened wait. This is recorded footage, not live playback, a cached response or a general latency promise. The browser reload demonstrates saved-data retention; it is not a filmed process restart.
Code
Source and setup: RishavRajSingh44/BountyPilot. The repository is public; the verified code baseline is 85d57a4.
Local browser → Next.js / TypeScript → GitHub REST API
├── local SQLite
└── local Ollama → Gemma 3 4B
Original application code is AGPL-3.0-only. You can inspect, run, modify and redistribute it under that license, including its source-sharing obligations. Ollama is separately MIT licensed; Gemma weights have Google's separate model terms. Fonts, icons, adapted components and source excerpts retain their own rights. Third-party notices explain those boundaries; no model weights are bundled.
After installing compatible Node 24 and Ollama, clone the repository and follow its platform-specific README configuration. The core commands are:
npm ci
ollama pull gemma3:4b
npm run dev
Start the Ollama runtime separately. The app runs on loopback for one trusted local user. Contributors can run deterministic tests without downloading a model, inspect the small server modules, and submit focused changes through CONTRIBUTING.md.
How I Built It
I initially considered a separate Python API. For this workload, another backend would mostly forward HTTP to Ollama while duplicating contracts. One Next.js/TypeScript application kept UI, validation and server-side integration together. SQLite provides local snapshots, analyses and saved statuses without a database service.
GitHub REST supplies the real issues and bounded discussion excerpts. Search does not run a model for every result. A deterministic score combines evidence signals, explicit skill matches, recency, availability and scope signals. Its breakdown is inspectable, applies to the fetched page, and is neither a payment probability nor expected earnings. Gemma cannot change that score or the payment-evidence state.
!Opportunity details with original source access and conflicting, unverified evidence
For analysis, I send bounded excerpts and skills to Gemma 3 4B, the open-weight model. Ollama is the inference runtime that loads and serves it. BountyPilot is the application orchestrating sources, validation, presentation and storage. This integration does not use the Gemini API.
The model must return structured JSON. The application checks the schema, source identifiers, unique citation identifiers, exact quote membership and risk-to-citation references. It records omitted or shortened context. Substantive input requires a valid citation, and malformed output gets at most one validation retry within the overall deadline.
I learned to draw a precise boundary around “grounded”: a quote can exist in a source while poorly supporting a conclusion. These checks establish quotation fidelity, not truth, semantic entailment or payment. Human source review remains part of the workflow.
!Historical Gemma 3 4B output showing model identity, estimates, questions and an exact source citation
Failure recovery mattered as much as the prompt. A failed analysis preserves the last successful result. Successful writes are transactional; a source refresh racing an analysis cannot silently attach an old answer to changed evidence. Cached results retain their original generation time. Timeouts and cancellation give the user a recovery path.
GitHub quotas produced another concrete lesson: an exhausted detail quota originally blocked searches even when search budget remained. Separating those cooldowns fixed unnecessary waiting, with a regression test protecting the distinction.
Here is the verified evidence, rather than a claim that every future issue will work:
| Check | Recorded result |
| --- | --- |
| Deterministic tests | 79 passed; 12 opt-in model cases skipped in ordinary runs |
| Chromium checks | 7 passed; synthetic HTTP boundaries, separate from live inference |
| Clean source installation | Fresh dependencies/browser installation and production build passed on macOS |
| Real-issue Gemma 4B analysis | 11.394 seconds on an M4 Pro Mac with 48 GiB RAM; one observed request |
| Gemma 1B grounding evaluation | All 12 cases failed validation; experimental only |
| Updated live discussion | Two 4B requests failed validation; neither was persisted as a successful analysis |
| Durability/recovery | Process-restart persistence and separate backup restoration passed integrity and content checks |
Published CI passed six Linux/macOS/Windows jobs across Node 24.14.0 and 24.21.0; each current-Node job also passed the seven browser checks. These automated runners do not establish Gemma performance on those operating systems. Release verification records commands, failures and limits.
The separate 12-case 4B prompt-v4 evaluation produced 12 valid outputs; manual assessment judged 11 useful. That small result did not prevent later live failures. I kept rejection as product behavior rather than weakening validation for a cleaner demo.
Codex helped with planning, implementation and checks. Ponytail FULL guided small changes and reviews of connected code. Neither replaced actual model runs, database checks or inspecting the resulting interface.
!Saved opportunities in BountyPilot's persistent local pipeline
Why Does Open Innovation Matter?
The strongest reason for this architecture is control over the decision-making process. A developer can inspect the ranking weights, see which excerpts reached the model, challenge an evidence heuristic, and change validation without waiting for a hosted vendor to expose an internal feature.
Local Gemma also makes the core analysis available without a mandatory paid LLM API or commercial AI subscription. With Ollama local, selected skills and excerpts go to a process on the user's machine. Personal snapshots and the pipeline stay in local SQLite by default. That database is plaintext, protected by operating-system permissions, not a promise of isolation from other software running as the same user.
Live GitHub discovery and refresh require internet. Once dependencies, weights and a snapshot are available, inference over retained content can run locally; the saved pipeline does not need GitHub to answer another request. Users can explicitly configure a secured remote Ollama endpoint, but that sends analysis input to its operator and changes the privacy assumptions. It is not an automatic fallback.
The 1B experiment made the value of inspectability concrete. A smaller download returned responses, yet failed all twelve grounding checks. I could preserve that evidence, label the option experimental and keep 4B primary. A successful local installation alone would have hidden the quality problem.
The tradeoffs remain real. My measured machine has 48 GiB RAM; I have not established a lower-memory minimum. Installing Node, Ollama and model weights takes effort and storage. Inference takes noticeable time. Models can echo supplied skills, omit important context or fail validation. Schema-valid results still need judgment. Local operation costs electricity and hardware even when no API invoice arrives.
I kept one small provider interface for future work, while deferring Gemini, OpenAI and other cloud adapters until after submission. That preserves room to extend the project without making a proprietary service a prerequisite. AGPL source access lets contributors reproduce failures, propose better evidence rules and improve the same local tool they run themselves.
Those freedoms apply to BountyPilot's software. Gemma remains an open-weight model under Google's separate terms, not an AGPL-licensed or OSI-approved model. Keeping that distinction visible is part of making an honest open project.
My Agent Session
<!-- Optional, but judges love it. Save your session with DevRelay and embed it with the agent_session tag (see the challenge page), or link to it. -->
My First User
<!-- Optional, but this is where bonus points live. Did someone use it? Who were they, what did they say, and did they pay? If not, remove this section. -->
!Image description
Prize Categories
Best Use of Gemma
Gemma 3 4B performs the opportunity analysis at the center of BountyPilot, served locally through Ollama. I am also entering the overall Side Hustle challenge. The aim is a useful decision aid developers can run, inspect and improve while deciding which coding opportunity deserves their time.
<!-- Team Submissions: Please pick one member to publish the submission and credit teammates by listing their DEV usernames directly in the body of the post. -->
<!-- Thanks for participating! -->
5 reactions
Try it on Linux, macOS or Windows 11 (Node.js 20.11+):
npx --yes run-spaceapp@latest install
Already running it:
npx --yes run-spaceapp@latest update
Release notes: github.com/oll4com/spaceapp/releases
9 views
Mentions
I built a self-hosted workspace to run AI coding CLIs side by side (MIT, OpenCode first) Hey r/selfhosted. I built this, so read it with that bias in mind. SpaceApp is a self-hosted web workspace for running AI coding CLIs side by side. Each CLI runs in its own pane, panes live in rooms, and the sessions persist, so a room is still there when you come back to it. Why I built it I was running coding agents in a pile of terminal windows and tmux splits, each with its own login and its own idea of which folder it was in. I wanted one place on my own machine where they all run, where I can see which one is waiting for me, and where nothing depends on somebody else's SaaS. It became the tool I work in all day. What a public install does today - 11 AI coding CLIs in the public installer, OpenCode first: OpenAI Codex, Claude Code, Gemini CLI, Qwen Code, Kimi Code, Grok Build, DeepSeek CLI (experimental), Cursor, GitHub Copilot and Autohand. Installable is not the same as tested: the public install rehearsal ran OpenCode end to end. Claude Code has its own install flow that you start yourself. - Up to 16 panes per room. - Your own AI accounts. Providers connect through their official login flows or a masked credential field. - The containers get no bind mount of your host filesystem by default. A folder shows up only after you register it as a workspace, and read-only mounts are supported. - Portable, checksummed backups of the app data, PostgreSQL and owner memory. Provider credentials, login state, workspace contents and browser profiles stay out of them. - Telemetry is opt-in, and No is the default. - MIT-licensed. What it is not - Rooms organize sessions; they are not a security boundary. Container separation is not a sandbox against a hostile workload. - It is not lightweight. The installer wants at least 4 CPUs, 8 GB RAM and 15 GiB of free disk. The standard profile, which adds a managed Chromium container, recommends 8 CPUs, 16 GB RAM and 25 GiB. - It listens only on its own host by default. A phone, a tablet or another computer can reach it once you make the install reachable yourself. - There are optional modules (copilots and studios) that a new install starts without. I'm leaving them out of this post: it is about the CLI workspace. How to try it Node.js 20.11 or newer, then: npx --yes run-spaceapp@latest install It runs on Linux, macOS and Windows 11. There's no compose file to copy: the launcher manages the containers itself (core API and web app, CLI runtimes, PostgreSQL, Temporal). The default light profile has no browser container; the standard profile adds it. After the readiness checks, a one-time setup token creates the first account, and you register the folders you want the agents to see. If you'd rather click around first, there is a live demo with invented data: spaceapp.dev Code: oll4com/spaceapp What I'd like to hear from you - Which CLIs you run together, and what a room should give you that a tmux session doesn't. - Where the install breaks on your hardware or OS. - Whether the backup split (what goes in, what stays out) matches what you would expect from a self-hosted tool. I'll be in the comments.
Logins stay out of its context: the Vault fills a login or a one-time code in the pane's browser, and Nous gets back a status, not the value. Self-hosted and MIT-licensed: oll4com/spaceapp
Show HN: SpaceApp – Run several AI coding CLIs side by side, self-hosted
One tool layer for eleven AI coding CLIs: what we learned building SpaceApp Why I built SpaceApp, in my own words: > I built it to be my main work tool. I'm a developer! And it became my main tool and work environment. I no longer need to open email, social media or any development program: absolutely nothing. I'm inside SpaceApp all day; I even watch YouTube videos and listen to music through SpaceApp. SpaceApp is a self-hosted workspace where AI coding CLIs (Claude Code, Codex, OpenCode, Gemini CLI and others) run side by side in persistent rooms. Each of them comes with its own config format, its own way of registering MCP servers, its own sign-in, its own "resume the last session" flag and its own idea of what Esc means. This post is about the two parts that turned out to matter most: where the agents' tools come from, and where the agents' sessions live. The N×M problem Every CLI wants its tools declared in its own file: one JSON shape for one, a TOML table for another, a command-line flag for a third. Multiply that by the tools you want every agent to have (a managed browser, shared memory, tasks, files, deployment checks) and you get N×M configurations that drift apart. Worse, a CLI usually starts every declared MCP server when the session starts, whether or not the agent ever calls it. Twelve panes × five servers is a lot of idle processes. Capability first, MCP on demand The first rule we settled on: use what the runtime already has. If the CLI has a shell, file editing and image input, the agent uses them directly. A tool layer that wraps cat in an MCP call only adds latency and tokens. For SpaceApp's own operations (status, logs, the managed browser, memory), the route depends on the runtime: - Codex and Claude Code panes register no MCP server at startup. When the agent needs a SpaceApp operation it calls one on-demand entrypoint, space-capability. It answers "what can I call and with which arguments" from cached, compact schemas without starting anything. The first real call starts a small worker on a pane-private Unix socket, and that worker starts only the backend that was asked for: at most three per pane, each stopped after an idle timeout. - Other runtimes keep a single dispatcher (space-mcp-dispatcher) declared once from a shared registry. The dispatcher also spawns registry servers only when called and stops them when idle. Two details turned out to matter more than the transport: 1. A declared tool grants nothing. Every operation still passes its own schema, role, approval and broker gates, whichever path it came through. MCP is a transport, not an authorization model. 2. No replays after ambiguity. If a call may have executed and the transport broke, the call is not retried automatically. A duplicated deploy check is annoying; a duplicated "send" is worse. The operator can override routing per runtime, model and tool (Automatic, Native only, Allow fallback, Disabled), and the settings page explains which route a call will take. This path is still marked experimental in the architecture doc: it is enabled for Codex and Claude Code only, because each runtime's capabilities have to be verified before it gets the on-demand path. Sessions that outlive the web server The second lesson: the process that serves the UI must not own the agents. In SpaceApp each CLI runs in a terminal session owned by a separate pane host process, not by the API. The API attaches to that session over a socket. When the API restarts during a deploy, or the browser drops its connection, the agent keeps working, and the pane re-attaches to the same session. The pane host tracks session generations and output sequence numbers, so a reconnecting view knows where it left off. That separation is also why we never resume a provider conversation by copying a transcript. If a CLI has a native resume (a session id, a thread id), we use it; if it does not, we say so instead of pretending. Eleven TUIs are eleven TUIs Things that broke along the way, in roughly the order they hurt: - Stop means different keys. Esc interrupts a turn in some CLIs, Ctrl-C in others, and one is stopped through its own server API. Stop now sends each TUI its own interrupt. - Model pickers are UI, not API. Switching a model in one CLI means driving its /model menu. In a short pane the menu shows a scrolled window of rows and clips its hints, so the picker has to read scroll markers and page through the menu. It also has to pick "this session only", never the saved default every other session shares. - "Ready" screens lie. An old, partial MCP startup message once kept a newer, ready Codex screen from being detected. Now a newer header supersedes the earlier partial marker, while a screen that is still loading keeps blocking. - Contracts are not end-to-end proof. A contract suite checks every declared start, resume and login command line of every declared runtime against the wrapper filter. That catches regressions in our adapters; it does not prove that the next upstream release of a CLI behaves the same. We track that gap openly. What it does not solve - Isolation between users. Rooms organise work; they are not a security boundary between mutually untrusted users. The Unix user is the outer trust boundary, so one install should serve one trust domain. - Your provider's view. Self-hosting keeps the workspace and its data on your host. The model providers you configure still receive what the agents send them. Try it SpaceApp is open source (MIT) and installs with: npx --yes run-spaceapp@latest install Architecture: github.com/oll4com/spaceapp/blob/main/docs/archit… · Known limitations: docs/quality/KNOWN_LIMITATIONS.md in the repository. If you run several coding agents on one codebase, I would like to hear how you keep their tools and sessions in order.
Six AI models played chess through their CLIs. None of them delivered checkmate. I build SpaceApp, a self-hosted workspace for running AI coding CLIs side by side. On 18 October I let six models play chess against each other there, each through its own CLI. Nobody won by checkmate. The way they lost is the interesting part. The setup - Players: Opus 5.5 and Fable 5.1 (through Claude Code), GPT-6-Astra and GPT-6.1-Sol (through Codex), all at xhigh reasoning; Gemini 4 Argon (High) and Gemini 3.8 Flash (High) (through the Gemini CLI). - Format: a 4-round Swiss tournament, 12 games. - Clock: 120 seconds per move. Each move is one one-shot turn of the model's CLI: it gets the position and answers with a move. - Strikes: a timeout, a provider error or a second illegal answer plays a random legal move for that side and counts a strike. The third strike forfeits the game. - Length: after 60 plies (30 moves each) the game is adjudicated on material. The results | # | Model | Points | W / D / L | Elo | Avg per move | Forfeits | |---|---|---|---|---|---|---| | 1 | Opus 5.5 (xhigh) | 3.5 | 3 / 1 / 0 | 1535 | 13 s | 0 | | 2 | Gemini 4 Argon (High) | 3.5 | 3 / 1 / 0 | 1535 | 23 s | 0 | | 3 | GPT-6-Astra (xhigh) | 2 | 2 / 0 / 2 | 1500 | 37 s | 2 | | 4 | Fable 5.1 (xhigh) | 1 | 1 / 0 / 3 | 1478 | 26 s | 3 | | 5 | Gemini 3.8 Flash (High) | 1 | 1 / 0 / 3 | 1477 | 40 s | 3 | | 6 | GPT-6.1-Sol (xhigh) | 1 | 1 / 0 / 3 | 1475 | 34 s | 3 | - Zero checkmates. 11 of the 12 games ended on a third strike. - The 12th game, Gemini 4 Argon against Opus 5.5, ran the full 60 plies and was drawn on material. - Opus 5.5 and Gemini 4 Argon were the only models that never needed a fallback move. Opus 5.5 was also the fastest, at 13 seconds per move on average. What it measures, and what it does not Twelve games are a small sample, and a tournament decided by forfeits says very little about chess strength. What it does show is how reliably a model answers through its CLI when every answer has a deadline: no timeouts, no provider errors, no illegal moves, game after game. That is the property I care about most when agents run unattended for hours, and it is not the same ranking you get from a single-prompt leaderboard. Try SpaceApp SpaceApp runs on Linux, macOS and Windows 11 with Node.js 20.11 or newer: npx --yes run-spaceapp@latest install It is source-available under the Business Source License 1.1. Code, docs and known limitations: oll4com/spaceapp If you have run something similar, I would like to know which models held up for you, and which rules you would change for a fairer second run.
6 AI models played a 4-round Swiss chess tournament, each through its own CLI in SpaceApp. 120 s per move; three missed moves lose the game. Zero checkmates: 11 of 12 games ended in a forfeit. Only Opus and Gemini 4 Argon never missed a move. oll4com/spaceapp t.co
I built SpaceApp: a self-hosted workspace that runs AI coding CLIs side by side in persistent rooms. OpenCode, Codex, Claude Code, Gemini, Qwen, Kimi, Grok and DeepSeek, each in its own pane, on your own machine. oll4com/spaceapp
From one command to a first page: what changed in SpaceApp's setup I build SpaceApp, a self-hosted workspace for running AI coding CLIs in isolated rooms. A room organizes work; each pane in it is an independent CLI or app session. The runtimes on offer are OpenCode, Codex, Gemini, Qwen Code, Kimi Code, Grok Build and an experimental DeepSeek CLI; Claude Code has its own owner-initiated installation flow. The latest setup releases were about one stretch of the experience: the time between running the installer and getting a first useful answer. Here is that path step by step, and what changed on it. 1. Install You need Linux, macOS or Windows 11, Node.js 20.11 or newer, and at least 4 CPUs, 8 GB RAM and 15 GiB of free disk. The standard profile, which adds a managed browser, recommends 8 CPUs, 16 GB RAM and 25 GiB. npx --yes run-spaceapp@latest install The launcher manages the core API and web app, the CLI runtimes, PostgreSQL and Temporal. The default light profile leaves out the managed Chromium; the standard profile starts a browser container as well. 2. Create the owner account Once the installation passes its readiness checks, you use a one-time setup token to create the first account, register the folders you want agents to work in, and connect providers through their official login flows or masked credential input. Host folders are visible only after you register them as workspaces, and read-only mounts are supported. New: the setup wizard's Ready step offers three first prompts you can copy. 3. The first model The first OpenCode pane starts on a free model, Big Pickle. A model counts as working only after a real tool call: it has to read a small file with its read tool and answer with the random token inside it. When Big Pickle fails that check, the newest free tool-capable models are tried instead. Free models can change upstream at any time. If the one you are on stops responding, pick another in OpenCode. 4. The first prompt Two things used to get in the way here. A room without a project had nowhere obvious to write. Now a CLI pane in such a room starts in /workspaces/<room>, so a first prompt like "build me a page" writes index.html there. The first task could stop at a permission prompt. On macOS, a first OpenCode task could wait at "Access external directory /tmp" until someone answered. Now OpenCode's first run authorizes only its own private scratch directory, and the bootstrap instructions send temporary files there. Other paths outside the workspace still ask. 5. Staying current The app now shows "Update available" when a newer release exists and opens the update command with a Copy button: npx --yes run-spaceapp@latest update What to know before you try it - SpaceApp is designed for one trusted owner on one self-hosted instance. - Backups are portable and checksummed and cover application data, PostgreSQL and owner memory. Provider credentials, login state, workspace contents and browser profiles are left out. - Telemetry is opt-in. Answer No and nothing is sent. Answer Yes and one anonymous event is sent the first time an agent finishes an answer (event, random id, version, CPU architecture, date). Feedback welcome I would like to hear where the first five minutes still break for you: your OS, the profile the installer picked, and the step where it stopped. The full release notes are here: github.com/oll4com/spaceapp/releases
I Built BountyPilot: An Open-Weight AI That Helps Developers Find Coding Opportunities Worth Their Time This is a submission for the Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle What I Built A GitHub issue with “$400” in its title looks like an opportunity. It still leaves the questions that determine whether I should spend a weekend on it: Is the work available? What counts as acceptance? Do I have the skills? Who can authorize payment? I built BountyPilot around that gap between finding a lead and deciding it deserves my time. It searches real public GitHub issues, retrieves their sources, and puts reward evidence beside the technical task. Local Gemma analysis adds a scope summary, relevant skills, estimated difficulty, suggestions, risks and unanswered questions. I can inspect the quoted sources, save an opportunity, and return to a persistent local pipeline. The distinction I kept coming back to was a reward claim versus verified funding. An amount, a label or a maintainer-associated comment cannot establish that money is available or that my contribution would qualify. BountyPilot exposes that uncertainty instead of presenting a list of promised earnings. !BountyPilot Discover showing real GitHub issues, filters and unverified reward evidence The intended customer is an independent developer or freelancer who repeatedly evaluates paid coding leads. The practical value would be less time piecing together scattered threads and fewer decisions made from a bounty title alone. Possible businesses around that workflow include managed setup and support, opt-in notifications, or paid hosting. Those are future possibilities, not implemented services or existing revenue. Hosted operation would need authentication, privacy boundaries and operating-cost work. I have no external-user or willingness-to-pay evidence yet. These are genuine October 16 application screenshots. The video below was recorded October 17 using another real issue; screenshot amounts and timings belong to their original captures. Every pictured reward remains an unverified source claim. Demo {% embed player.mux.com %} Watch the completed 88-second BountyPilot demo. The public player opens without sign-in. Select full screen for the interface text; the video is captioned and silent. It shows real GitHub discovery, opening oll4com/spaceapp#60, reviewing source/payment evidence, selecting local Gemma 3 4B, generating an analysis, inspecting difficulty and citations, saving, and reloading Pipeline. The recorded request performed new inference, measured at 14.537 seconds by the application. I removed 8.4 seconds of waiting and added reading holds; the edit discloses the shortened wait. This is recorded footage, not live playback, a cached response or a general latency promise. The browser reload demonstrates saved-data retention; it is not a filmed process restart. Code Source and setup: RishavRajSingh44/BountyPilot. The repository is public; the verified code baseline is 85d57a4. Local browser → Next.js / TypeScript → GitHub REST API ├── local SQLite └── local Ollama → Gemma 3 4B Original application code is AGPL-3.0-only. You can inspect, run, modify and redistribute it under that license, including its source-sharing obligations. Ollama is separately MIT licensed; Gemma weights have Google's separate model terms. Fonts, icons, adapted components and source excerpts retain their own rights. Third-party notices explain those boundaries; no model weights are bundled. After installing compatible Node 24 and Ollama, clone the repository and follow its platform-specific README configuration. The core commands are: npm ci ollama pull gemma3:4b npm run dev Start the Ollama runtime separately. The app runs on loopback for one trusted local user. Contributors can run deterministic tests without downloading a model, inspect the small server modules, and submit focused changes through CONTRIBUTING.md. How I Built It I initially considered a separate Python API. For this workload, another backend would mostly forward HTTP to Ollama while duplicating contracts. One Next.js/TypeScript application kept UI, validation and server-side integration together. SQLite provides local snapshots, analyses and saved statuses without a database service. GitHub REST supplies the real issues and bounded discussion excerpts. Search does not run a model for every result. A deterministic score combines evidence signals, explicit skill matches, recency, availability and scope signals. Its breakdown is inspectable, applies to the fetched page, and is neither a payment probability nor expected earnings. Gemma cannot change that score or the payment-evidence state. !Opportunity details with original source access and conflicting, unverified evidence For analysis, I send bounded excerpts and skills to Gemma 3 4B, the open-weight model. Ollama is the inference runtime that loads and serves it. BountyPilot is the application orchestrating sources, validation, presentation and storage. This integration does not use the Gemini API. The model must return structured JSON. The application checks the schema, source identifiers, unique citation identifiers, exact quote membership and risk-to-citation references. It records omitted or shortened context. Substantive input requires a valid citation, and malformed output gets at most one validation retry within the overall deadline. I learned to draw a precise boundary around “grounded”: a quote can exist in a source while poorly supporting a conclusion. These checks establish quotation fidelity, not truth, semantic entailment or payment. Human source review remains part of the workflow. !Historical Gemma 3 4B output showing model identity, estimates, questions and an exact source citation Failure recovery mattered as much as the prompt. A failed analysis preserves the last successful result. Successful writes are transactional; a source refresh racing an analysis cannot silently attach an old answer to changed evidence. Cached results retain their original generation time. Timeouts and cancellation give the user a recovery path. GitHub quotas produced another concrete lesson: an exhausted detail quota originally blocked searches even when search budget remained. Separating those cooldowns fixed unnecessary waiting, with a regression test protecting the distinction. Here is the verified evidence, rather than a claim that every future issue will work: | Check | Recorded result | | --- | --- | | Deterministic tests | 79 passed; 12 opt-in model cases skipped in ordinary runs | | Chromium checks | 7 passed; synthetic HTTP boundaries, separate from live inference | | Clean source installation | Fresh dependencies/browser installation and production build passed on macOS | | Real-issue Gemma 4B analysis | 11.394 seconds on an M4 Pro Mac with 48 GiB RAM; one observed request | | Gemma 1B grounding evaluation | All 12 cases failed validation; experimental only | | Updated live discussion | Two 4B requests failed validation; neither was persisted as a successful analysis | | Durability/recovery | Process-restart persistence and separate backup restoration passed integrity and content checks | Published CI passed six Linux/macOS/Windows jobs across Node 24.14.0 and 24.21.0; each current-Node job also passed the seven browser checks. These automated runners do not establish Gemma performance on those operating systems. Release verification records commands, failures and limits. The separate 12-case 4B prompt-v4 evaluation produced 12 valid outputs; manual assessment judged 11 useful. That small result did not prevent later live failures. I kept rejection as product behavior rather than weakening validation for a cleaner demo. Codex helped with planning, implementation and checks. Ponytail FULL guided small changes and reviews of connected code. Neither replaced actual model runs, database checks or inspecting the resulting interface. !Saved opportunities in BountyPilot's persistent local pipeline Why Does Open Innovation Matter? The strongest reason for this architecture is control over the decision-making process. A developer can inspect the ranking weights, see which excerpts reached the model, challenge an evidence heuristic, and change validation without waiting for a hosted vendor to expose an internal feature. Local Gemma also makes the core analysis available without a mandatory paid LLM API or commercial AI subscription. With Ollama local, selected skills and excerpts go to a process on the user's machine. Personal snapshots and the pipeline stay in local SQLite by default. That database is plaintext, protected by operating-system permissions, not a promise of isolation from other software running as the same user. Live GitHub discovery and refresh require internet. Once dependencies, weights and a snapshot are available, inference over retained content can run locally; the saved pipeline does not need GitHub to answer another request. Users can explicitly configure a secured remote Ollama endpoint, but that sends analysis input to its operator and changes the privacy assumptions. It is not an automatic fallback. The 1B experiment made the value of inspectability concrete. A smaller download returned responses, yet failed all twelve grounding checks. I could preserve that evidence, label the option experimental and keep 4B primary. A successful local installation alone would have hidden the quality problem. The tradeoffs remain real. My measured machine has 48 GiB RAM; I have not established a lower-memory minimum. Installing Node, Ollama and model weights takes effort and storage. Inference takes noticeable time. Models can echo supplied skills, omit important context or fail validation. Schema-valid results still need judgment. Local operation costs electricity and hardware even when no API invoice arrives. I kept one small provider interface for future work, while deferring Gemini, OpenAI and other cloud adapters until after submission. That preserves room to extend the project without making a proprietary service a prerequisite. AGPL source access lets contributors reproduce failures, propose better evidence rules and improve the same local tool they run themselves. Those freedoms apply to BountyPilot's software. Gemma remains an open-weight model under Google's separate terms, not an AGPL-licensed or OSI-approved model. Keeping that distinction visible is part of making an honest open project. My Agent Session <!-- Optional, but judges love it. Save your session with DevRelay and embed it with the agent_session tag (see the challenge page), or link to it. --> My First User <!-- Optional, but this is where bonus points live. Did someone use it? Who were they, what did they say, and did they pay? If not, remove this section. --> !Image description Prize Categories Best Use of Gemma Gemma 3 4B performs the opportunity analysis at the center of BountyPilot, served locally through Ollama. I am also entering the overall Side Hustle challenge. The aim is a useful decision aid developers can run, inspect and improve while deciding which coding opportunity deserves their time. <!-- Team Submissions: Please pick one member to publish the submission and credit teammates by listing their DEV usernames directly in the body of the post. --> <!-- Thanks for participating! -->
Try it on Linux, macOS or Windows 11 (Node.js 20.11+): npx --yes run-spaceapp@latest install Already running it: npx --yes run-spaceapp@latest update Release notes: github.com/oll4com/spaceapp/releases