Skip to content

Target directory tree — the Wave C crossing

This is the floor plan for the new product: what sits on a machine once Qren is installed, and how the code that produces it is arranged. It also lists, line by line, where every part of today’s system lands — carried across, replaced by something better, or dropped — because nothing moves over merely by existing. Five choices are locked here, among them a single identity for the application, shipping the working parts inside the application rather than installing them separately, and one database per workspace so a client’s data can be lifted out as a single folder. The last section is the one-off move an existing machine goes through, with a full backup taken before anything is touched. The shape is approved; each individual piece still gets its own conversation before it actually crosses.

Status: approved in shape (operator, 2026-08-20) — D1–D5 locked. Nothing moves unexamined; each piece crosses after its own conversation. To be recorded as ADR 0006 when the first piece moves.

Governed by: ADR 0002 (appliance), ADR 0003 (one app / one service / one grant), ADR 0004 (engines via ACP), the rename ledger’s Wave C section, and vision laws 8–9 (earns-its-place; evolve / adopt / design-fresh).

Four things are defined here, in order: (1) what is on disk on an installed machine, (2) the repo that produces it, (3) the crossing map — where each part of today’s AOS lands, with a default lane, (4) the decisions this forces. §5 is the migration sketch.


(what is actually on the disk of a machine once Qren is installed)

Three install profiles. Every path below is tagged with the profiles that have it: A appliance, M member, D dev.

1.1 /Applications/Qren.app — the product [A M D]

Section titled “1.1 /Applications/Qren.app — the product [A M D]”
Qren.app/Contents/
Info.plist bundle id: ai.qren.app (D1)
MacOS/
Qren the app — Tauri shell (React UI)
qren-supervisor the ONE LaunchAgent executable
qren CLI — thin Control API client
Resources/
runtime/ the embedded runtime (D2) — immutable, signed,
python/ relocatable CPython + locked site-packages
bin/ adopted native components: whisper.cpp /
sherpa-onnx, restic, whatsmeow, …
qren/ the Python package (engine + workers,
see §2 runtime/)
skills/ framework skills
agents/ core agent definitions
defaults/ default config + modules/crons manifests
migrations/ instance-layer migrations, versioned
arms/ bundled arm packages (empty in year one)
Library/LaunchServices/
ai.qren.helper privileged helper [A only] — SMAppService
root daemon; absent from member builds
Frameworks/ , _CodeSignature/

One signed artifact carries UI + supervisor + CLI + runtime. One version number per instance. The Tauri updater (already pointed at qren.ai) replaces the whole thing atomically; the Supervisor restarts its children after.

1.2 ~/Library/Application Support/Qren/ — plumbing [A D; M minimal]

Section titled “1.2 ~/Library/Application Support/Qren/ — plumbing [A D; M minimal]”

Replaces ~/.aos. Invisible, Mac-native, never browsed by a human.

Application Support/Qren/
instance.yaml identity: instance id, profile (appliance|member|dev),
engagement credential *reference* (token in Keychain),
appliance hostname, drive_root
config/ operator-level config, every key writable ONLY via
the Control API: operator.yaml, trust.yaml,
modules.yaml (overrides of bundle defaults), crons.yaml,
connections/ (non-secret parts)
data/
instance.db instance scope: principals, workspaces, bindings,
engagement state, audit log, approval queue
workspaces/<ws-id>/ per-workspace state (D4)
workspace.db work, chat, people, comms, memory, events
index/ search + vector indexes (rebuildable)
agents/<agent-id>/ per-agent: persona overlay, session state, scratch
connections/<conn>/ connector state (cursors, caches) — secrets in Keychain
engines/<engine>/ Qren's adapter state per engine (claude|codex|kimi|local):
pinned version, last probe, projection manifest (§1.5)
models/ downloaded ML models, content-addressed manifest
(whisper, VAD, embeddings, Kokoro voices)
arms/<arm-id>/ installed arm packages + their state
shadow/ restic repo config + exclude rules (ciphertext goes to R2)
run/ supervisor-owned ephemera: control.sock, pids, locks,
per-worker sockets (cleared on boot)
updates/ staged app updates

Following Apple conventions, two things live beside it, not inside:

~/Library/Logs/Qren/ supervisor.log, workers/<name>.log, audit mirror
~/Library/Caches/ai.qren.app/ purgeable: transcode temp, thumbnail, HTTP caches

Member profile gets only instance.yaml, config/, run/, and Logs — the member app is a client of an appliance; it keeps no workspace data.

The only folder a human sees. Pure files — no dotfolders, no metadata inside it; everything about the Drive is in App Support.

~/Qren/
Personal/ the owner's personal workspace
Knowledge/ markdown brain (stages 1–6), human- and agent-editable
Log/ what happened
Files/
<Workspace Name>/ one folder per workspace homed on this appliance
Knowledge/ Log/ Files/ Meetings/

drive_root is configurable per instance (this Mini: on AOS-X). Members reach the Drive through the app over portal/tailnet; no local copy, no sync engine (ADR 0002). A File Provider extension is a later option, not a plan.

1.4 Engines — outside Qren’s tree, by design [A D]

Section titled “1.4 Engines — outside Qren’s tree, by design [A D]”

Claude Code, Codex CLI, Kimi CLI install where their vendors put them (~/.local/bin/claude, ~/.claude/, ~/.codex/, …) on the vendor’s own login. Qren never relocates or wraps them; it probes them (ADR 0004) and projects into them.

(how Qren tells each coding assistant about its skills and agents, without moving or changing the assistant itself)

Each engine reads skills/agents/hooks from its own config dir. The adapter for that engine writes an idempotent, manifest-tracked projection there — e.g. ~/.claude/skills/qren-* → symlinks into Qren.app/…/runtime/skills/, hook entries in ~/.claude/settings.json; AGENTS.md + config.toml fragments for Codex. The manifest (engines/<engine>/projection.json) makes removal exact. This is the only place Qren writes outside its own tree.

1.6 Secrets and connections — who holds the keys [A]

Section titled “1.6 Secrets and connections — who holds the keys [A]”

(passwords and access tokens never sit with a person; the machine in the office holds them all)

No human ever receives a password, and no secret ever lives on a member machine. The appliance holds every credential, because the appliance does the work:

  • Each connection (Gmail, calendar, accounting, ad platform…) is granted by the person who owns the account — they log in through the portal on their own identity; the resulting token lands in the appliance’s secure store (Keychain, service ai.qren), scoped to one workspace and one granting principal.
  • The admin panel shows every connection as a row: Gmail — granted by Faisal — workspace RUNREC — last used 2h ago — revoke. Revoke is a row removal, instant.
  • An agent acting on a connection acts under that grant and is audited as such (“agent X read mail via Faisal’s grant”). Agents never see raw tokens; the runtime resolves them at call time.
  • Non-secret connection state (cursors, caches) lives in workspaces/<ws-id>/connections/<conn>/; the secret never leaves Keychain.
  • Member machines store exactly one secret: the member’s own Qren login.
  • Connections are commands with one standard shape (auth resolved from Keychain, JSON in/out, engine-agnostic). MCP only where a vendor offers nothing else.

(the part of macOS that starts background programs; Qren registers exactly one, plus one privileged helper on an office machine)

~/Library/LaunchAgents/ai.qren.supervisor.plist [A M D] KeepAlive + RunAtLoad
/Library/LaunchDaemons/ai.qren.helper.plist [A] via SMAppService

Nothing else. Every current com.aos.* / am.hish.qren-* job becomes a supervised child (§3). Member machines run the supervisor too — it is what holds the portal/tailnet connection and the optional capture helper.


(the single store of code and documents that everything above is built from)

One monorepo. Top-level directories are deliberate; each is one deliverable or one shared layer. site/ and workers/ move under edge/.

qren/
CLAUDE.md CONTEXT.md README.md LICENSE
docs/
adr/ context/ design/ runbooks/ runbooks = deployment playbook
app/ Qren.app — Tauri shell (from aos-app: src/, src-tauri/)
supervisor/ Rust crate — the LaunchAgent: process tree, admission
control, scheduler (replaces crons.yaml jobs), health,
Control API server (control.sock), updater hand-off,
engine adapters' process side
cli/ Rust crate — `qren`: Control API client + dev tooling
core/ Rust crate shared by app/supervisor/cli: instance +
workspace data layer (from aos-app/workspaces.rs),
permission guard, audit, Control API types
runtime/ Python — what crosses from aos/core
pyproject.toml uv.lock
qren/
work/ people/ comms/ knowledge/ council/ loop/ notify/ util/
workers/ long-running children: bridge, transcriber, capture,
indexer, crawler, mesh
engines/ ACP adapters: claude/ codex/ kimi/ local/
migrations/
skills/ framework skills (SKILL.md dirs)
agents/ core agent definitions
arms/ arm packages — format is design-fresh; starts empty
helper/ privileged helper (SMAppService daemon)
edge/ everything on Cloudflare, one account
site/ qren.ai (Pages) ← moves from /site
portal/ login-first portal app (Pages)
workers/
broker/ invites, machines, engagement credentials ← from aos-app/broker
releases/ /download, /updater, /releases ← from /workers/releases
relay/ chat relay + push (ADR 0005)
concierge/ edge concierge over the replication set
packaging/ DMG, signing, notarization, updater manifests,
release.sh (from aos-app/scripts), appliance profile
vendor/ vendored third-party source (ontology, whatsmeow)
tests/ cross-cutting (unit tests live beside code)

Not created until they have contents: mobile/ (iOS), arms/*, edge/portal, edge/workers/relay, edge/workers/concierge. Empty directories are a promise the tree cannot keep.

The structural inversion. Today the app shells out to ~/aos/.../cli.py and launchctl directly (lib.rs). After the crossing the app is a client of the Supervisor over control.sock; so is the CLI; so are Qren Ops and the chief of staff via the tunnel. No component reaches around the Supervisor to touch the runtime, launchd, or the databases. This is law 7 (Control API) made physical.


(part by part: what comes over from today’s system, what is replaced by something better, and what is left behind)

Default lane per area. The lane is the presumption the per-file review starts from, not its verdict — law 8 still applies to every file.

Lanes: EVOLVE crosses (refactored into the new shape) · ADOPT is replaced by a graded component · DIE does not cross · OPEN lane to be set at review.

Today (~/aos/core/… unless noted) Lane Lands in
engine/work (+ qareen/ontology it vendors) EVOLVE runtime/qren/work, schema into core/
engine/people, engine/people/intel EVOLVE runtime/qren/people
engine/comms (bus, channels, drafts, extract, patterns) EVOLVE runtime/qren/comms — channels become connections
engine/comms/{sentinel,envoy,converse,autonomous} OPEN core comms vs. first arm material — decide at review
engine/intelligence (ingest, compile, topics, lint) EVOLVE runtime/qren/knowledge
engine/council, engine/loop, engine/notify, engine/util EVOLVE runtime/qren/…
engine/arms EVOLVE seed for arms/ format work
services/bridge (Telegram/Slack) EVOLVE runtime/qren/workers/bridge — a connection, not the primary chat (ADR 0005)
services/transcriber, services/companion, services/listen ADOPT workers/transcriber, workers/capture over whisper.cpp / sherpa-onnx; torch venvs die
services/memory (indexer/watcher) ADOPT workers/indexer over LanceDB / qmd — decide at review
services/whatsmeow (Go) EVOLVE vendor/whatsmeow source → runtime/bin/ binary
services/mesh, mesh/ EVOLVE workers/mesh (Tailscale embedding terms still open)
services/crawler, ~/.aos/services/n8n, slack-lite OPEN likely DIE; crawler maybe an arm primitive
services/work_runner, services/converse supervisors DIE absorbed by the Supervisor
skills/ (44) EVOLVE skills/ — each SKILL.md reviewed individually
agents/ (chief, steward, advisor) EVOLVE agents/ — chief → chief of staff; steward’s health role → Supervisor
hooks/ EVOLVE runtime/qren/engines/claude/ (engine-specific by nature)
infra/integrations/* + registry.yaml EVOLVE connections layer; Google/GitHub/Apple-native cross, each reviewed
infra/migrations, infra/reconcile EVOLVE runtime/qren/migrations; reconcile checks → Supervisor health
infra/lib OPEN per-module
qareen/screen (web UI), qareen/api, qareen/services DIE replaced by the app
qareen/{actions,queue,events,proactive,overnight,pipelines} OPEN mine for Supervisor scheduler / approval queue; most dies
qareen/tracking (auto-tracker) OPEN first external arm candidate, not core
qareen/voice, qareen/screen/designs DIE superseded by companion v1 design cycle
bin/cli/* (30 commands) EVOLVE qren <subcommand> — Control API calls; scripts that are only glue DIE
bin/crons/*, config/crons.yaml EVOLVE Supervisor scheduler entries
bin/internal/release-manager, ~/aos-releases, install.sh, bootstrap.sh DIE Tauri updater + DMG; appliance bootstrap is packaging/
onboarding/, steer/ OPEN onboarding rebuilt once against the app (ADR 0003)
config/modules.yaml (schema 2) EVOLVE runtime/defaults/modules.yaml — kind/tier/health model survives
config/templates, config/defaults EVOLVE runtime/defaults/
apps/, templates/, DESIGN.md OPEN
~/.aos/config/* (50 files) EVOLVE collapse into instance.yaml + config/ — every key must earn a place
~/.aos/data/*.db (work, people, comms, qareen, workspaces, actions, aos) EVOLVE instance.db + workspaces/<id>/workspace.db (D4)
~/.aos/services/* venvs (4.5 GB) DIE runtime is in the bundle
~/.aos/{meetings,models,work,cache,logs} EVOLVE Drive Meetings/, models/, workspace state, Caches, Logs
~/.aos/{islah,content-engine,ios-deploy,headscale,…} OPEN mostly DIE or personal-stays-personal
13 com.aos.* plists + am.hish.qren-{door,tunnel} DIE supervised children
~/vault OPEN becomes Drive Personal/{Knowledge,Log} + per-company workspaces (D3)
aos-app src/, src-tauri/ EVOLVE app/, workspaces.rscore/
aos-app broker/, approval-broker/, scripts/ EVOLVE edge/workers/broker, supervisor/ (approval queue), packaging/
qren site/, workers/releases, demos/ EVOLVE edge/site, edge/workers/releases, demos/ stays
Personal machine things: adhan, hish.am publishing, Quran Garden services not Qren; stay on this Mini outside the product

D1 — Bundle identity becomes ai.qren.app (labels ai.qren.supervisor, ai.qren.helper; Keychain items QREN_*). The ledger reserved this for “Wave C with intent” — this is the intent. Cost: TCC grants reset once per machine (15 friendly installs, 0 clients — never cheaper). Alternative: keep am.hish.aos forever under a product called Qren; rejected — every client audit would ask why.

D2 — Runtime ships inside the bundle, not in App Support. ADR 0003 §4 lists “runtimes” under App Support; this amends it. Rationale: one signed artifact, one version per instance (Ops must answer “what is client X running” with one number), the venv-rot defect class (the reason modules.yaml grew a venv probe) becomes impossible, and the updater already exists. Cost: every runtime fix is an app release — that is how products behave. Dev escape hatch: qren dev link points Resources/runtime at a repo checkout on profile D.

D3 — The vault becomes Drive content. ~/vault/knowledge + log~/Qren/Personal/; nuchay and elora-greens collections → their own workspaces’ Knowledge/. Recommended, but as a later wave: the first crossing symlinks ~/Qren/Personal/Knowledge → ~/vault/knowledge so Obsidian, qmd collections, and habits survive untouched.

D4 — One database per workspace. instance.db for instance scope; workspaces/<id>/workspace.db for everything tenant-scoped. Tenancy is then enforced by the filesystem as well as the guard; “walk with your data” is cp -r of one directory; restic can shadow per workspace. Amends the Slice-0 decision (single workspaces.db) — the schema stays, the file splits.

D5 — site/ and workers/ move under edge/. Minor; requires the Pages GH Action path update in the same commit.

Not decided here (stay open per the vision): arm format, correction loop, Tailscale embedding terms, chief-of-staff name.


5. Migration sketch — one atomic wave per install

Section titled “5. Migration sketch — one atomic wave per install”

(the one-off move an existing machine makes, done in a single pass rather than piece by piece)

Scripted, run by the new Supervisor on first launch when it finds ~/.aos:

  1. Snapshot ~/.aos (tar) to Application Support/Qren/updates/pre-crossing/.
  2. Unload every com.aos.* / am.hish.qren-* agent; remove plists.
  3. Write instance.yaml; translate ~/.aos/config/* through the key map (migration module); untranslated keys are reported, not carried.
  4. Move databases per D4 (schema migration, then split by workspace id).
  5. Move models/, meetings/ → Drive, logs → ~/Library/Logs/Qren/.
  6. Re-key Keychain AOS_*QREN_* (copy, verify, delete old).
  7. Run engine projection for each detected engine; remove old ~/.claude symlinks that pointed into ~/aos.
  8. Health pass; only then ~/aos symlink and ~/aos-releases are removed — after operator confirmation, per the destructive-ops rule.

Data-plane ids (KV aos_ user ids, invite tokens, machine ids, db contents) are never renamed — ledger rule stands.