An accessibility-led public downstream of yc-software/qm: a multiplayer agent harness for work on the web, in Slack, and on a self-hosted Matrix homeserver.
Clone or share this fork at github.com/csullivan84/qm.
The upstream project has a strong shared-agent architecture. This fork focuses that architecture on individuals and the smallest teams who need screen-reader access, self-hosted chat, current tooling, and a deployment path that does not assume a large platform budget.
Original architecture and most of the code come from upstream QM and its contributors. This repository retains that Git history and the MIT copyright notice. The changes below are the deliberate IronVitae downstream work.
- Matrix as a first-class surface. QM can receive and answer messages through a self-contained Matrix homeserver. Homeserver, room, and user allowlists fail closed; Matrix identities map explicitly to QM principals; sync cursors and delivery transaction IDs are durable; retries do not duplicate replies.
- Private-room security checks. The bot requires invite-only rooms, joined-only history, forbidden guest access, and an authorized membership set before handling plaintext. Encrypted rooms are rejected rather than silently mishandled.
- Screen-reader accessibility. Web and portal changes improve landmark timing, route focus, dialog naming, reply-state announcements, conversation labels, and keyboard navigation. The companion IronVitae Matrix client adds skip links, semantic room and message regions, polite live announcements, and immediate announced room joins.
- Modern runtime stack. The fork pins Node 26.5.1 and npm 12.0.2, uses TypeScript 6.0.3 and Vite 8.2, updates Pi and the broader dependency graph, and keeps container builds reproducible on the same runtime.
- Pi plus OpenRouter in production. The live deployment uses the Pi harness with an OpenRouter model while preserving QM's harness abstraction for OpenCode, Codex, and Claude Code.
- Known-working Podman deployment. The fork is running as Podman containers on Lifepod, giving one person or a very small team a practical self-hosted path without taking on a large orchestration platform.
- Simple Lifepod source sync. A clean committed branch can be pushed and materialized as an immutable, exact-SHA checkout on Lifepod with one command instead of a hand-built SSH archive transfer.
Matrix support currently has deliberate limits:
- It connects one controlled homeserver and accepts only explicitly allowlisted private rooms and users.
- It handles plaintext messages. End-to-end encryption, edits, attachments, and reactions are not processed; encrypted rooms are rejected rather than silently mishandled.
- Matrix configuration is operator-managed through environment settings and a private access-token secret. The admin portal cannot yet add rooms, change allowlists, replace the token, or reload Matrix without a core restart as it can for Slack.
- Federation is not required. Slack still has broader interactive features, so Matrix parity is an active direction rather than a claim that every Slack affordance already exists.
Most agents are designed like personal assistants. You can make one work for a whole company, but it quickly gets complex. QM is designed for startups. Employees each get their own isolated workspace and work independently without affecting each other, and they can also collaborate with the agent in channels, group messages, and projects.
Each person and each room has its own scoped memory, files, keychain view, permissions, crons, web apps, and durable sandbox.
It's built with open source in mind. Pick your own harness and model and switch between them — Pi, OpenCode, Codex, and Claude Code all drive the same core, so a deployment isn't tied to any single vendor.
- Personal and shared scopes. People customize the agent to be theirs, and still work with it collaboratively in Slack channels and projects.
- Slack and web. The same identity and configuration carries between Slack and the web app.
- Admin control. Set org-level configuration, a security posture, and which harnesses and models are available.
- Web apps. Spin up custom internal apps and publish them to the right people.
- Shared skills. Skills are scope-owned and shareable by grant, with admin-gated promotion to the whole org and skill packs imported from git repositories.
- Background work. Crons and watches run work while nobody's watching.
- Search internal notes, email, documents, databases, and the web together
- Retrieve information from your company brain
- Build internal apps, publish them to the right people, and keep their data current
- Learn your writing voice from past sends, then triage your inbox on a schedule — labels and reply drafts included
- Work in an existing repository: run tests, open PRs, monitor CI, check system logs
- Track a project in a shared channel and post updates and follow-ups
flowchart LR
DB[("Postgres<br/>sessions · memory · queue")]
subgraph CORE["Headless core"]
API["API · identity · policy · scheduler"]
LOOP["Agent loop<br/>(Pi, OpenCode, Claude Code)"]
API <--> LOOP
end
SBX["Per-scope sandbox<br/>files · tools · logged-in services"]
DB <--> API
LOOP <--> SBX
Every turn runs through a central core, which can use a variety of models and harnesses
to generate the response. A Postgres persistence layer holds user data, session history,
and other durable state. The agent has a small, fixed tool surface; one of those tools is
execute, which runs commands in the scope's own isolated sandbox — its durable computer,
where installed tools stay installed. The web UI, the admin panel, and the public portal
are optional plugins over the core's HTTP API;
Slack is an optional in-process plugin that core starts
and supervises through a direct service client.
The core runs TypeScript directly on Node and uses Fastify for HTTP. The Slack plugin uses Bolt; the web UI builds with Vite and renders with Lit.
The core itself is generic. Everything specific to one company — org config, custom tools
and skills, sandbox image, infrastructure — lives in a deployment directory that the
qm CLI validates and deploys. Every substrate (harness, session
store, sandbox, memory) sits behind an interface, so production implementations swap in
via one wiring file.
QM's approach follows local coding agents like OpenCode, Codex, and Claude Code: the agent acts as the person it's working for, with their credentials and permissions, and everything it does is audited. An org picks one security posture, which narrower scopes can only tighten:
- Strict — every harness tool call pauses for human approval, except the two no-effect turn enders.
- Auto (default) — a classifier screens provenance-labelled external data and tool results before they reach the model; a deployment can point that at its own screening proxy.
- Dangerous — no content screening, no pauses between tool calls.
The predeclared command policy — approval rules and hard denials for things like recursive deletes or destructive SQL — applies in every posture, Dangerous included.
SECURITY.md has the threat model, the operator assumptions, and the
known limitations.
Node 26.5.1 and npm 12.0.2 are required.
git clone https://github.com/csullivan84/qm.git
cd qm
npm ci
npm run typecheck
npm testCopy .env.example to a private environment file, configure the surfaces and harnesses you
intend to use, then run npm start. The full upstream deployment-directory contract remains documented in
deployment.md.
With the lifepod SSH alias configured and a clean branch checked out:
npm run sync:lifepodThe command pushes the current branch to this repository, updates a bare mirror at
/home/canyon/src/qm.git, and creates an immutable checkout such as
/home/canyon/builds/qm-1a2b3c4d. It refuses dirty worktrees, non-fast-forward pushes, mismatched remote
commits, and occupied checkout paths. It syncs source only; container builds and service restarts remain
separate, deliberate production steps.
Operators can override the defaults with QM_LIFEPOD_HOST, QM_PUBLIC_REPO_URL,
QM_LIFEPOD_MIRROR, QM_LIFEPOD_BUILD_ROOT, and QM_LIFEPOD_BRANCH.
Issues and focused pull requests are welcome for accessibility, Matrix, self-hosting, and current-runtime
work. Changes intended for yc-software/qm should also follow upstream's
CONTRIBUTING.md. Report vulnerabilities privately through this repository's GitHub
Security Advisory flow and follow SECURITY.md; do not open a public vulnerability issue.
The local Git remote named upstream tracks yc-software/qm; origin tracks this public downstream.
Upstream changes can be merged normally, but this fork intentionally does not keep core byte-identical:
Matrix, accessibility, runtime, and Lifepod operations are product differences. Generally useful fixes can
still be proposed upstream after removing deployment-specific details.
docs/getting-started.md— first run, end to endcli/README.md— theqmCLI and the deployment directory contractdocs/deploy-directory.md— the deployment directory in full.env.example— every knob, documented in placeplugins/— the surfaces (Slack, web UI, admin, portal)
Except where otherwise noted, QM is available under the MIT License.
