Openclaw (draft)
Prepared for: Mark · Saad Mohsin · Hasaan Jamil Date: May 2026 Status: Living document — updated as decisions are made
A company knowledge fabric — a governed library of research, datasheets, manuals, contact records and executive memory — that our agents can query safely. Each user gets a personal agent reachable from Cliq and email. All personal agents, plus dedicated task agents, share one library and one shared set of rules. Nothing important gets said twice, nothing sensitive leaks, and every retrieval has a clean audit trail.
- ✅ Done: One personal agent per user, auto-created
- Thin orchestrator — handles conversation, routes tasks to sub-agents or librarians
- Authenticates to Zoho via OAuth2 through Warden
- Can only access what the user can access in WorkDrive and CRM
- Personal memory, workspace, and preferences fully isolated per user
- When user's Zoho account is disabled, their token is revoked immediately via the UI
Warden is a standalone Next.js + shadcn/ui application running on Ionos. It is the single source of truth for all OAuth tokens, user permissions, and connection state in the OpenClaw system. Named deliberately — Warden controls who gets access to what, and nothing passes through without its approval.
Warden does four things:
- Syncs users from Zoho Directory so only known, active employees can authorise agents
- Restricts scopes per user — new users get a safe default set of restricted scopes, and admins can grant exceptions individually
- Owns all tokens — refresh tokens and cached access tokens live in Warden's encrypted database, never in agent workspace files or environment variables
- Serves agents — OpenClaw agents call Warden's internal API on every Zoho operation; Warden returns a cached or freshly refreshed access token, or sends back an OAuth URL if the user hasn't authorised yet
No token ever lives in a workspace file, an environment variable, or an agent's memory beyond the duration of one task. Warden handles everything.
Token typeWhere storedRuleRefresh tokenUI Postgres DB (encrypted column)Never leaves the UI. Only UI backend reads/writes it.Access tokenUI Postgres DB (encrypted column)Refreshed inline before returning to agent.Agent receivesAccess token in API response onlyHeld in agent memory for session only. Never written to disk or workspace files.Sub-agent receivesAccess token as env var from parentSingle task use. Discarded on completion. Sub-agents never call UI directly.
Admin: Scope pre-approval
Before any user connects, admin configures the maximum scopes each user's agent may request. The OAuth URL the agent generates will only include pre-approved scopes — the user cannot grant more than the admin allows.
Admin: Connections dashboard
Main view — all users × all services, live status:
Admin: Per-connection detail
Full scope list, token expiry countdown, request log (timestamp / agent / endpoint / outcome), revoke button, re-authorise button.
Admin: Revoke
One click. Warden calls Zoho OAuth revoke endpoint, deletes tokens from database, marks connection revoked. Agent gets not_connected on its next request immediately.
Admin: Request log
Every agent token request logged — timestamp, user, agent ID, service, endpoint, outcome. Filterable by any field. Full audit trail.
User: /connect page (public)
The page users land on when their agent sends them an auth URL. Shows the service being connected and the exact scopes that will be granted. One Authorise button. After callback completes, user sees a success page and returns to Cliq.
EndpointCalled byPurposeGET /api/internal/tokenOpenClaw agentsGet access token for a user+service. Returns token or auth URL.POST /api/internal/log-requestOpenClaw agentsLog a Zoho API call outcome.GET /api/oauth/callbackZoho (redirect)Exchange auth code for tokens, store in DB.POST /api/oauth/revokeAdmin UIRevoke a connection.
The UI runs a Node.js cron job (same process) every 45 minutes. Loops through all active connections, refreshes any token expiring within 90 minutes, logs outcome. On failure, marks connection expired — agent gets not_connected on next call.
Personal agents and task agents are thin orchestrators. When a task needs specialist capability, they spawn an isolated sub-agent that loads only what it needs.
Each sub-agent has its own TOOLS_[DOMAIN].md and MEMORY_[DOMAIN].md. It knows nothing about other domains, users, or credentials outside its task. When the parent passes a Zoho access token to a sub-agent, the sub-agent uses it for the task and discards it — no path to refresh or re-request.
AgentPurposeAgent 1 — Market IntelligenceDaily briefings, web research, email to distribution listAgent 2 — Product KnowledgeProduct Q&A via Cliq + email + SalesIQAgent 3 — Website / Support / Sales TriageSalesIQ chatbot, Zoho Desk, inbound sales filterAgent 4 — Lead Generation ResearchProspect lists, writes CSV to staging, no outreachAgent 5 — Social Media & ContentDraft-only, writes to review folder, human approves
Based on OpenClaw Memory Architecture Spec v1.0, May 2026.
The core principle: an agent should not re-read the entire history of its relationship with a user on every turn. It remembers the broad strokes (identity, user profile), the recent session (digest from today), and looks up specific prior context on demand — never loading everything just in case.
TierWhat it isSizeWhen loaded1 — IdentitySOUL.md, IDENTITY.md, TOOLS.md~1–2 KBAlways. Prompt-cached.2 — User profileUSER.md per user, preferences~1–2 KBAt session start, cached per user.3 — Working memoryCurrent conversation, raw, in context windowGrows during sessionLive during session only. Compacted at ~40% of model window (older messages summarised, most recent 10 kept verbatim).4 — Session digestsStructured summary written at session end~500–1,000 tokensToday's digest loaded at session start. Older digests queried on demand only.5 — Long-term semanticEvery digest, artifact, decision — semantically searchableUnboundedNever by default. Queried on demand via memory_search.
Default context at session start: ~5 KB. Identity + user profile + today's digest only.
At session start:
- Load Tier 1 (identity) — cached, cheap
- Load Tier 2 (user profile) — cached per user, cheap
- Load Tier 4 (today's digest if it exists) — gives same-day continuity
- Nothing else. No older history, no prior-day conversations.
During the session:
-
User references prior context ("we discussed X last week") → agent calls
memory_search -
Agent judges it should check proactively → calls
memory_search - Only the relevant snippet (~0.5–2 KB) is injected — not the whole digest
- Working memory grows. At ~40% of model window → compaction kicks in
At session end:
- Agent generates a structured digest (Haiku call — cheap)
-
Digest written to WorkDrive at
90_Agent_Memory/digests/{agent}/{user_id}/YYYY-MM-DD.md - Catalog row added to Postgres
- Digest embedded and written to Qdrant per-user collection
- Raw working memory discarded
Persistent footprint per session: ~0.5–1 KB of structured data.
One digest per session per user. Target size: 500–1,000 tokens. Structured fields, not prose.
FieldContentsTopicsShort bulleted list of subjects discussed. One line each.DecisionsWhat was decided, why, who decided, when.Open itemsOutstanding items — what, owner, deadline, status.EntitiesPeople, companies, projects, events, locations mentioned. CRM/catalog links where they exist.FilesPointers to artifacts produced (WorkDrive links, not content).SourcesExternal URLs and internal WorkDrive IDs cited.
Example digest (abbreviated):
A week later when Mark says "remind me about that November Madrid event" — the agent searches digests, retrieves this, answers in one short response. Cost: a few hundred tokens. Not 264K tokens of replayed history.
Every agent gets one new tool for accessing long-term memory.
Under the hood:
- Broker validates: this agent is allowed to read this user's memory. Logs the call.
- Catalog (Postgres) queried first — cheap structured filter on topics/entities to narrow
- Qdrant queried against the user's per-user collection — semantic match
- Matching snippets returned with metadata (date, source digest, confidence)
- Agent injects only the 1–2 most relevant snippets into context
Total cost of a search: a few hundred tokens of embedding + a few hundred tokens of returned snippet. Not 87K of raw history.
When the agent should call it:
- User references prior context — "we discussed", "you said", "last week", "the document I sent"
- Topic comes up the agent thinks it may have seen before
- Before answering a question that depends on a prior decision
WorkDrive — content
Holds the actual files: identity files, user profiles, daily digests, artifacts, attachments.
Per-user folders inherit WorkDrive ACLs. Mark's digests are not readable by Saad's agent because Mark's folder is not accessible to that service account. Permissions enforced by WorkDrive, not application code.
Postgres (catalog) — metadata
One row per memory item. Agent queries catalog first to decide what to load.
Qdrant — semantic search
Embeddings of digest and artifact text. Per-user collections enforce isolation.
The biggest risk in this design is memory leakage between users. Defence is layered:
- WorkDrive ACLs — each user's folder accessible only to that user's service account. First line of defence at the OS level.
-
Per-user Qdrant collections —
memory_{mark_id}is a separate collection frommemory_{saad_id}. No cross-user query can return cross-user results — credentials are collection-scoped. -
Broker enforcement — every
memory_searchcall passes through the broker, which validatesuser_idmatches the active user before any store is touched. - Audit — every memory access logged with caller, subject, query, and what was returned. Cross-user attempts surface as alerts.
Target: cross-user retrieval count = 0. Any non-zero result is an incident.
MetricTargetDefault context at session start≤ 5 KBPer-turn context (typical)5–25 KBPer-turn context (with retrieval)≤ 40% of model windowDigest size500–1,000 tokensmemory_search latency< 500 msmemory_search false-negative rate< 10%Cross-user retrieval count0
FolderWho can readWhat goes in00_Public_KnowledgeAll staffApproved external material, datasheets, press10_Internal_OperationsAll staffInternal templates, process docs, general reference20_Commercial_IntelligenceCommercial teamMarket research, prospect dossiers, strategy30_Product_and_SupportProduct, support, customer-facingManuals, datasheets, service docs40_Executive_and_BoardExec onlyBoard reports, financing, personnel-sensitive40_Executive_and_Board/00_Highly_RestrictedTightly restrictedContracts under negotiation, live legal matters50_Compliance_and_GovernanceCompliance + adminConsent, suppression, DSARs, retention60_ArchiveInherits from source domainSuperseded versions90_Librarian_IntakeAdmin onlyStaging area for inbound material90_Agent_MemoryPer-user ACLsAgent identity files, user profiles, session digests
Applied to all governed Team Folders:
FieldTypeNotesDomainChoice (mandatory)Five values matching the scoped librariansSensitivityChoice (mandatory)Public / Internal / Confidential / Restricted / Highly_RestrictedStatusChoice (mandatory)Draft / Active / Archived / PendingDocTypeChoicedatasheet / manual / briefing / research_report / board_report / contract / process_doc / otherDocSubmittedViaChoicedirect_upload / email_intake / cliq_intake / api / ingestion_syncDocContributorEmailEmailAuto-populated from OAuth identity at submission. Audit only.DocSummaryMulti-line text≤ 300 words. Not generated for Highly_Restricted.TagsSingle-line textFree-form, comma-separatedEffective dateDateWhen document becomes canonicalReview dateDateNext scheduled reviewContains personal dataChoiceNone / Low / Medium / High
Each librarian owns one domain. They never read outside it.
LibrarianScopeQdrant collectionPublic & Marketing00_Public_Knowledgecol_public_marketingProduct & Support30_Product_and_Support + public subsetcol_product_supportCommercial & Research20_Commercial_Intelligencecol_commercial_researchExecutive & Board40_Executive_and_Board (except Highly Restricted)col_executive_boardCompliance & Contacts50_Compliance_and_Governance + CRM governance metadatacol_compliance
If a question crosses domains, the broker calls each librarian separately and merges — librarians never talk to each other directly.
Runs as an n8n workflow. Every request from a personal agent or task agent passes through it — nothing reaches a librarian or Qdrant directly.
What it checks on every request:
- Who is the user? (from OAuth2)
- Which agent is asking?
- What is the purpose?
- What is being requested?
- Any special flags? (personal data, bulk operation, suppressed contact)
Then decides: allow in full / allow with redaction / summary only / require human approval / deny. Every decision is logged.
/connect public page — user-facing OAuth landing./api/oauth/callback — exchange code, store tokens in encrypted DB./api/internal/token — agent token endpoint./api/internal/log-request — agent request logging endpoint.90_Agent_Memory folder in WorkDrive with full subfolder structure.memory_items table in Postgres with schema and three indexes.- [ ] n8n workflow "Session Digest Writer" — triggered on session end, calls Haiku to produce structured digest
- [ ] Workflow writes .md to WorkDrive, catalog row to Postgres, embedding to Qdrant
- [ ] Dead-letter queue for failed digest writes — no silent loss
- [ ] Error alerting if digest pipeline fails
- [ ] n8n workflow "Session Digest Writer" — triggered on session end, calls Haiku to produce structured digest
- [ ] Workflow writes .md to WorkDrive, catalog row to Postgres, embedding to Qdrant
- [ ] Dead-letter queue for failed digest writes — no silent loss
- [ ] Error alerting if digest pipeline fails
-
[ ] Implement
memory_search— catalog query (Postgres) then Qdrant query, return snippets - [ ] Register as tool in all relevant agents
- [ ] Broker allow rules per agent for memory access
- [ ] Audit logging on every call
- [ ] Update agent session-start: load Tier 1 + Tier 2 + today's digest only
- [ ] Remove any auto-loading of older history
- [ ] Proactive memory_search check when user references prior context at session start
- [ ] Keep daily-reset in place during Phases A–D (safety net)
- [ ] Once digest pipeline is reliable for 1 week → switch daily-reset off
- [ ] Monitor: 10 sessions/week for 2 weeks — check context retrieval and token usage
- [ ] Create all Team Folders with correct permissions
- [ ] Apply Librarian classification Data Template to all governed folders
- [ ] Configure Data Template schema per Section 7
- [ ] Postgres catalog database on Ionos (document catalog schema)
- [ ] Qdrant installed on Ionos
- [ ] 5 librarian Qdrant collections with separate credentials
- [ ] Embedding model chosen and locked — model name written to catalog per chunk
- [ ] Ingestion pipeline: WorkDrive scan → text extract → classify → catalog + Qdrant write
- [ ] Data Template field population step in ingestion worker
-
[ ] Archive intake pipeline: email/Cliq → stage in
90_Librarian_Intake→ classify → review queue - [ ] Policy broker workflow with allow/deny rules
- [ ] Nightly background jobs: reclassification, retention sweeps, contact freshness
- [ ] Classification review queue
- [ ] Archive intake review queue
- [ ] Contact adjudication queue (15k-name dedup)
- [ ] Outreach approval queue
- [ ] Archive and rollback view
- [ ] Agent activity log
- [ ] Policy editor
- [ ] DSAR workflow
- [ ] Personal agent memory inspector (view/clear per user with audit record)
- [x] OpenClaw running on Ionos ✅
- [x] cliq-router (Haiku front-door) ✅
- [x] Email pipeline with Haiku classifier ✅
- [x] Per-user personal agents with OAuth2, auto-created ✅
- [ ] Agent 1 — Market Intelligence
- [ ] Agent 2 — Product Knowledge
- [ ] Agent 3 — Website / Support / Sales Triage
- [ ] Agent 4 — Lead Generation Research
- [ ] Agent 5 — Social Media & Content
- [ ] Librarian: Public & Marketing
- [ ] Librarian: Product & Support
- [ ] Librarian: Commercial & Research
- [ ] Librarian: Executive & Board
- [ ] Librarian: Compliance & Contacts
- [ ] Sub-agent workspaces: linkedin-agent, docs-agent, research-agent, email-agent, crm-agent
- [ ] Load 15,000-contact list to staging table
- [ ] Normalise + deduplicate against Zoho CRM
- [ ] Build decision API: may_I_contact, company lookup, capped prospect list
- [ ] Apply suppression, lawful basis, freshness rules
- [ ] Network policy on Ionos: broker-only path to librarians/Qdrant/WorkDrive (enforced at network level)
- [ ] Prompt injection sanitiser in ingestion pipeline
- [ ] Append-only audit log for all queries, policy decisions, memory access, denied requests
- [ ] Highly Restricted subfolder — no Qdrant indexing, no embedding, catalog minimum only
- [ ] Step A: Agent 1 live — one real daily briefing emailed to distribution list
- [ ] Step B: Agent 2 + Product librarian + Mark's personal agent — one real support question answered from a manual
- [ ] Step C: Memory architecture Phases A–B — digest pipeline live, session-start protocol updated
- [ ] Step D: Warden live — agents can request Zoho access, users authorise via the connect page
- [ ] Agent 5 + Public & Marketing librarian
- [ ] Compliance librarian + CRM reconciliation + decision API → Agent 4
- [ ] Commercial & Research librarian
- [ ] Memory architecture Phases C–E — memory_search live, daily-reset retired
- [ ] Agent 3 (external-facing, higher stakes)
- [ ] Team agents (customer-success channel first)
- [ ] Executive librarian (last — highest sensitivity)
DocumentRelationship to this planOpenClaw Memory Architecture Spec v1.0Section 6 of this document is the full implementation of that specToken Consumption Analysis v1.1Diagnosed conversation-history-in-context as root cause. Memory architecture is the permanent fix.Librarian Agent How-To Guide v007The catalog + Qdrant + WorkDrive three-store pattern and broker enforcement apply equally to agent memory (Section 6) and the shared library (Sections 7–9). Same mechanism, different content.