diff --git a/docs/competitive/qm-2026-08.md b/docs/competitive/qm-2026-08.md new file mode 100644 index 00000000..add4f757 --- /dev/null +++ b/docs/competitive/qm-2026-08.md @@ -0,0 +1,313 @@ +# Competitive analysis: qm (yc-software/qm) + +**Date:** 2026-08-01 · **Analyzed revision:** `7f2c916` (repo HEAD at time of analysis) +**Subject:** — "A multiplayer agent harness for work. In Slack and on the web." (MIT) + +> **TL;DR** — qm is a serious, unusually mature competitor in exactly omadia's +> category (self-hosted multiplayer org agent, BYO LLM key). It is not a launch +> prototype but visibly operated production code that YC published as a snapshot. +> It is strong where omadia is thin (per-user isolation, code-execution sandbox, +> Slack, credential lifecycle) and weak where omadia is strong (privacy, answer +> verification, deterministic workflows, UI, plugin distribution, EU/GDPR +> positioning). Eight concepts were filed for potential adoption: +> [#575](https://github.com/byte5ai/omadia/issues/575)–[#582](https://github.com/byte5ai/omadia/issues/582) +> (label `from-qm`). + +--- + +## 1. Snapshot + +| | | +|---|---| +| Published | 2026-07-29 (three days before this analysis) | +| Traction | ~3,300 stars, ~300 forks, 41 open issues after 3 days | +| License | MIT | +| Size | ~75k LOC source, ~90k LOC tests (`src/` 342 files, `test/` 372 files), plus ~70k LOC CLI/plugins/scripts | +| Stack | Node 24 TypeScript run directly (type-stripping, no build step), Fastify, Postgres (+ pg-boss), Slack via Bolt, web UI via Vite + Lit | +| History | A single squash commit — the public repo is a snapshot of an internally operated system | +| Positioning | "Designed for startups": each employee gets an isolated workspace; collaboration happens in Slack channels, group DMs, and projects | + +qm's `package.json` self-description is telling: *"Headless core for the shared +org agent."* The core is headless; Slack, web UI, admin, and portal are plugins. + +## 2. Quality assessment + +This is the most striking part of the analysis. Signals of genuine production +maturity, rare for a three-day-old repo: + +- **Test ratio 1.2 : 1** (90k test LOC vs. 75k source LOC), CI sharded across 5 + runners with a shard-plan check, separate Postgres suite, e2e suite, live + Slack suite with screenshot gallery, 8 smoke scripts. +- **Zero `@ts-ignore` / `@ts-expect-error` / `eslint-disable`** in the entire + source tree; 13 comment lines total (their AGENTS.md mandates zero comments — + and it is actually enforced, not just claimed). `strict` + + `noUncheckedIndexedAccess`. +- **Interface-first throughout**: every store exists as an in-memory and a + Postgres implementation behind one interface; harness, sandbox, session + store, and memory are all swappable via one wiring file. +- **Operational scar tissue**: blue-green drain via build-SHA heartbeats, leader + leases with lost-lease handling, DNS-rebinding-aware egress authorization, + shadow-mode screening for classifier cutover, pre-migration + capability-loss computation for sandboxes, fail-open-with-audit patterns. + These are details written after incidents, not before launch. +- **An unusually honest `SECURITY.md`** that concretely names twelve known + limitations (command policy is bypassable, sandbox credentials are plaintext + while in use, audience-floor filtering has gaps, etc.). +- Supply-chain hygiene: npm `min-release-age=7` cooldown; per-user and per-org + spend budgets and rate limits built into the core config surface. + +Weaknesses exist but are not structural: a 2,800-line orchestrator god-file, no +schema migration framework (expand-only DDL spread over 23 store files), a +74-method config-store interface with a sync-cache-vs-durable dualism, and a few +RAM-only components that contradict their own "durable by default" rule. + +## 3. Architecture + +### 3.1 Scopes — the single isolation primitive + +Everything hangs off `ScopeId = ":"` with +`kind ∈ {personal, channel, group, team, org}`. Every conversation resolves +deterministically to exactly one scope. Per scope: memory, files, keychain +view, permissions, crons, deployed apps, and a durable sandbox. Resources are +"artifacts" with an owner scope; sharing happens exclusively via grants (no +transitive re-sharing). + +The standout idea is the **audience floor**: in shared rooms, effective +permissions are the *intersection across everyone present* — egress allowlist = +intersection, denylist = union; a shared file or credential handle only works if +every participant can reach its owner scope; rendered context is filtered per +recipient, with non-entitled tool results replaced by a stub that preserves +tool-call IDs so transcripts stay valid. (Filed as +[#575](https://github.com/byte5ai/omadia/issues/575).) + +### 3.2 Harness abstraction + +qm runs **Pi, OpenCode, Codex, and Claude Code** against the same core through a +four-bucket `Harness` interface (profile / turns / models / tools). The +**tool surface belongs to the core, not the harness**; adapters only present it +in their transport form, and policy callbacks (screening, approval gating) are +injected per turn. Capability differences are declared, not implicit. Pi is +clearly the first-class path (~4.5k LOC vs. ~1k for the others), and their own +SECURITY.md admits adapter gaps (OpenCode key handling, one path bypassing the +model gateway). + +### 3.3 Durable per-scope sandbox + +Each scope owns a persistent "computer" where installed tools stay installed; +the agent reaches it through an `execute` tool. Three interchangeable backends: +Fly.io Sprites, AWS Firecracker microVMs, local Docker. Read-only layers (org +files, skills) are materialized by content-hash fingerprint. A per-scope routing +table supports two live backends and migration, with capability-loss computed +before a move. Sandbox egress goes through a CONNECT proxy whose per-sandbox +capability JWT carries the egress policy itself as a claim; the authorizer +re-resolves DNS and re-checks IPs. (Filed as +[#576](https://github.com/byte5ai/omadia/issues/576).) + +### 3.4 Security model + +Three org-level postures reduced to two orthogonal axes (inbound screening × +tool approvals): `dangerous` (neither), `auto` (default: screening of +provenance-labelled external content), `strict` (no screening, human approval on +every tool call — screening is redundant when a human gates everything). Scopes +may only tighten against the org floor. + +Screening is built on **provenance labelling**: everything that is not direct +human input is bundled into `{source, content}` pairs with typed source tags +(`sender`, `prior-turn:user:`, `overheard:`, `attachment:`, +`tool_result:`, surface tags), judged either by an LLM with a prompt that +explains each tag's semantics, or by an external screening proxy (chunked, +shadow-mode capable). A `strict` verdict quarantines the turn before it runs; +unscreenable input **fails open with evidence** — an audit event plus a visible +"NOT security-screened" marker in the prompt. (Filed as +[#579](https://github.com/byte5ai/omadia/issues/579).) + +The **command policy** is a shell-*normalizing* parser, not naive regex: +quote/ANSI-C unwrapping, recursion into `$(…)`, backticks, pipes-to-shell, +here-strings; an org floor hard-denies recursive deletes, force-pushes, +destructive SQL, fork bombs in every posture including `dangerous`. Their +framing is honest: "a speed bump against mistakes and injection, not a sandbox +boundary." (Filed as [#580](https://github.com/byte5ai/omadia/issues/580).) + +### 3.5 Memory + +Deliberately minimal: one `MEMORY.md` notebook per scope with a 40-line "memory +line grammar" (dated bullets, normalization as dedupe key, 300-fact cap, +recency-biased recall cap). Postgres variant is an append-only revision log with +history and restore. Recall/capture policy per scope (`off | writable | +visible`). Cross-scope CC mirrors channel facts into the speaker's personal +memory with **trusted-provenance tagging — the model cannot forge provenance** +(model-supplied provenance markers are neutralized on write). Consolidation runs +on an explicit `UPDATE/DELETE/ADD` action grammar. + +### 3.6 Background work and operations + +Crons (5-field cron or interval, pg-boss with singleton keys, fresh thread per +fire with continuity only via workspace disk + fire log — explained to the model +verbatim), process **monitors/watches** over sandbox process sessions, a +31-line wake router (`engage | steer | drop`), and a run queue with +lease/claim/reap and heartbeat-based worker failover. Deliveries carry +idempotency keys and provenance. Multi-instance coordination via advisory-lock +leader leases; blue-green handover via build-SHA heartbeats that flip the old +instance into drain mode. + +## 4. Surfaces and ecosystem + +### 4.1 Plugins + +Strict two-tier coupling: Slack runs in-process; web UI, admin, portal, and auth +are **separate processes speaking only signed HTTP** to the core (HMAC +source-auth, replay nonces). A 279-line zero-dependency `chassis` package is the +only shared layer. Admin authority lives in exactly one durable store +(`admin_grants`), re-checked everywhere, fail-closed. Magic-link claims and rate +limits are durable in core Postgres so blue-green deploys cannot resurrect a +used link. The public portal is a from-scratch reverse proxy (OIDC + PKCE, +allowlisted headers, no cookie forwarding, path-traversal rejection). + +### 4.2 Slack depth + +The deepest surface: DMs, channel threads, and group DMs; thread-follow after a +single mention with a turn-detection that can stay silent or react with an +emoji; bidirectional reactions (base36-encoded message ids, full emoji +normalization); **approval flows as Block Kit buttons** (Allow once / session / +always / Deny) wired to exact-command grants; a channel-agent → personal-agent +handoff (`ask-agent` DMs the person for consent, then runs with *their* setup); +out-of-band file blobs (~1 GB) with SSRF refusal for externally hosted files; +guest/external-audience detection with ephemeral-only replies. Identity maps +Slack and web onto the same principal via verified work email, with a +refuse-to-start probe when email identity cannot be established. + +### 4.3 Skills + +Scope-owned artifacts with lifecycle (`draft → reviewed → published → +archived`), HMAC-signed manifests, scope-ordered resolution with shadowing +(personal beats org), grant-based sharing, **admin-gated promotion** into org +scope (direct creation there is refused), a guard that blocks automations from +editing shared skills, and **skill packs imported from git repos** (pinned or +tracked, trust tiers, SSRF-guarded fetcher, archived-not-deleted removals). +Eighteen seed skills ship in the box; the strongest teach the agent *processes* +(publish-and-verify, interactive login survival, email voice profiles, +broker-mediated credential use) rather than facts. (Filed as +[#577](https://github.com/byte5ai/omadia/issues/577).) + +### 4.4 Credentials + +Three distinct paths: (a) OAuth connectors for seven providers with per-provider +setup guides, one-time consent links minted by the core (the agent never builds +the URL, tokens never reach the browser); (b) a **keychain** with encrypted +credentials, grants (`once | standing`, audience scope, purpose, expiry, +revocation), "asks" (the agent requests a credential from its owner), and a +**broker** for org-wide service credentials — the agent names credential + +request and the core stamps the secret onto the wire, so the agent never holds +it; (c) resident sandbox logins (`gh`/`glab`/`gcloud` probes, device-flow +credential capture into the keychain and restore onto fresh machines, +credential paths kept out of workspace backups, Playwright storage state per +principal for persistent browser logins, secret-drop browser pages instead of +pasting secrets into chat). (Filed as +[#578](https://github.com/byte5ai/omadia/issues/578).) + +### 4.5 Internal web apps + +A single core primitive `publish({dir, entrypoint, name})` turns a workspace +directory into a running internal app: immutable versions, stable link, pointer +rollback, `$DATA_DIR`-only durable state, grant-based sharing, a dedicated apps +domain, and an injected edit widget that reopens the authoring UI and prompts on +new versions. The seed skill forbids passing off a file as a running app and +requires curl-verifying before declaring success. (Filed as +[#581](https://github.com/byte5ai/omadia/issues/581).) + +### 4.6 Deployment model + +A committed, portable **deployment directory** governed by an explicit contract +(`qm.config.jsonc`, `contract: 1`) with the CLI as its only interpreter: one +public URL from which all service URLs derive; secrets *computed* from enabled +services via typed specs and routed to exactly the consuming task; gate order +`check → doctor → build → plan → up → live drift check`; immutable image/config +pins; DB snapshot before AWS deploys with the restore point printed on +rollback. The headline move: **`qm init` materializes a deployment *skill*** so +an agent performs and verifies the deployment ("do not stop at infrastructure +health: complete the acceptance checks"). Orgs customize via private forks +(plain clone, never GitHub fork) where everything org-specific lives under +`deploy/layers//` and two skills maintain the upstream boundary in both +directions. (Filed as [#582](https://github.com/byte5ai/omadia/issues/582).) + +## 5. Comparison with omadia + +### 5.1 Shared ground + +Same category, near-identical pitch: self-hosted org agent, several people +sharing the same agents in team channels, BYO LLM key, vendor neutrality, +plugins, skills, scheduled background work, memory, audit, budgets, admin +surface. Both projects are notably honest about security limitations in their +public docs. + +### 5.2 qm has, omadia lacks + +1. **A per-person/per-room isolation primitive.** omadia's multiplayer rides on + channel-native session scopes; there is no personal workspace, no per-scope + keychain view, and no mixed-audience permission semantics. qm's scope + + audience-floor model is the strongest single concept in the repo. +2. **A code-execution sandbox.** omadia has no `execute` path at all — native + tools are a closed set and `ctx.scratch` is a plugin temp dir. qm's durable + per-scope computer is the substrate for half of its feature set. +3. **Slack, deeply.** omadia's README lists Slack among its channels, but Slack + exists in the code only as a type placeholder — while it is qm's main stage, + including approvals, reactions, and agent-to-agent handoffs. Until our Slack + channel ships, this is a direct competitive exposure and the README claim + should be tightened. +4. **Credential lifecycle** (grants, asks, broker, device-flow persistence) + versus our comparatively static vault. + +### 5.3 omadia has, qm lacks + +1. **Privacy Shield v4** — raw tool results never reach the model, with + per-turn privacy receipts. qm sends everything through (screened, not + minimized); model providers see the data. +2. **Answer verification** — claim extraction, deterministic checks, evidence + judging against the knowledge graph. qm has no counterpart. +3. **Deterministic compute paths** — Office document generation computed + server-side from dataset rows, not hallucinated through the model. +4. **Conductor** — a deterministic workflow engine with role batons, human + steps, deadlines, and fallbacks. qm's crons/monitors are simpler primitives. +5. **A plugin store and remote registries** (hub) with an install UX; qm has no + plugin distribution story beyond the deployment directory. +6. **The canvas UI** (omadia-ui, 24-primitive wire protocol) versus qm's + conventional chat-plus-sidebar web app. +7. **Subscription-based Claude CLI harness** (Pro/Max instead of API keys) and + the EU/GDPR positioning (AVV disclosure, "made in the EU"). + +### 5.4 Honest mirror + +The analysis of qm also sharpened three self-observations, recorded here so we +act on them: our "Slack/Discord" channel claims are ahead of the code (see +5.2.3); "signed plugin distribution" is sha256 + TLS integrity pinning, not +artifact signatures, and our wording should say so; and our plugins run +in-process in the kernel while qm's surfaces are separate signed-HTTP processes +— a real isolation gap to weigh against our install UX. + +## 6. Adoption candidates + +Filed as issues, label `from-qm`, ordered by architectural urgency: + +| Issue | Concept | Why | +|---|---|---| +| [#575](https://github.com/byte5ai/omadia/issues/575) | Scope model + audience floor | Makes the multiplayer promise enforceable; composes with Privacy Shield | +| [#576](https://github.com/byte5ai/omadia/issues/576) | Durable per-scope sandbox + `execute` | Our biggest functional gap; substrate for real work | +| [#577](https://github.com/byte5ai/omadia/issues/577) | Skills as scope-owned artifacts | User/team-authored skills without plugin packaging | +| [#578](https://github.com/byte5ai/omadia/issues/578) | Keychain grants + credential broker | Extends the Privacy Shield philosophy to credentials | +| [#579](https://github.com/byte5ai/omadia/issues/579) | Security postures + provenance screening | Injection-screening tier we currently lack | +| [#580](https://github.com/byte5ai/omadia/issues/580) | Shell-normalizing command policy | Prerequisite for any `execute` tool | +| [#581](https://github.com/byte5ai/omadia/issues/581) | `publish` primitive for internal apps | Durable micro-apps, complementary to the canvas | +| [#582](https://github.com/byte5ai/omadia/issues/582) | Deployment-directory contract, agent-run deploys | Reproducible deployments operated by omadia agents | + +#575 and #576 touch architecture decisions that should be settled early; the +rest can be adopted incrementally. + +## 7. Method + +Analysis performed 2026-08-01 against a fresh shallow clone of +`yc-software/qm@7f2c916` by three parallel code-exploration passes (core +architecture; surfaces/plugins/skills/deployment; omadia feature map as the +comparison baseline), plus direct reading of `README.md`, `SECURITY.md`, +`AGENTS.md`, and repository metadata. Claims about qm cite its source layout as +of that revision; claims about omadia were verified against this repository and +its sibling repos (omadia-ui, omadia-hub) at their then-current state.