Target directory tree — the Wave C crossing
In short
Section titled “In short”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.
1. Installed layout
Section titled “1. Installed layout”(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 updatesFollowing 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 cachesMember profile gets only instance.yaml, config/, run/, and Logs —
the member app is a client of an appliance; it keeps no workspace data.
1.3 ~/Qren/ — the Drive [A D; not M]
Section titled “1.3 ~/Qren/ — the Drive [A D; not M]”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.
1.5 Engine projection
Section titled “1.5 Engine projection”(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.
1.7 launchd
Section titled “1.7 launchd”(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 SMAppServiceNothing 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.
2. The repo — hishamalhadi/qren
Section titled “2. The repo — hishamalhadi/qren”(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.
3. The crossing map
Section titled “3. The crossing map”(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.rs → core/ |
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 |
4. Decisions this forces
Section titled “4. Decisions this forces”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:
- Snapshot
~/.aos(tar) toApplication Support/Qren/updates/pre-crossing/. - Unload every
com.aos.*/am.hish.qren-*agent; remove plists. - Write
instance.yaml; translate~/.aos/config/*through the key map (migration module); untranslated keys are reported, not carried. - Move databases per D4 (schema migration, then split by workspace id).
- Move
models/,meetings/→ Drive, logs →~/Library/Logs/Qren/. - Re-key Keychain
AOS_*→QREN_*(copy, verify, delete old). - Run engine projection for each detected engine; remove old
~/.claudesymlinks that pointed into~/aos. - Health pass; only then
~/aossymlink and~/aos-releasesare 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.