Status: Refined v0.3
Generated: 2026-06-28
Audience: Product, engineering, architecture, security, and platform teams
Primary objective: Define a next-generation, self-hosted AI assistant platform that combines multi-user governance, personal knowledge systems, project workspaces, durable agent execution, rich artifacts, BYOK model access, MCP/connectors, skills, slash commands, and third-party messaging channels.
Refinement notes (v0.2 — 2026-06-28): This revision (a) eliminates the
skill.yaml/SKILL.mdduplication in favor of the single-SKILL.mdAgent Skills standard, (b) narrows the technical stack to one coherent TypeScript stack matching the existingllamemonorepo, (c) separates author-declared skill capabilities from platform-assigned trust, (d) collapses the provider / search / vector / sandbox option menus to one MVP default plus one documented scale swap, and (e) adopts a Postgres-first posture (§24.0) — queue, scheduler, cache, locks, sessions, vectors, and full-text all live in one PostgreSQL, removing Redis, BullMQ, and the separate scheduler service from MVP (object storage stays a filesystem volume by default). Assumption: the platform is built on the existing Next.js / NestJS / TypeScript monorepo, not greenfield — if that is wrong, §23 inverts. Design opinions beyond the two refinement asks are parked under §33.1 ("Further suggestions"), not applied silently.
This product is a self-hosted, multi-user AI operating layer rather than a simple chat UI. It should support individuals, families, small organizations, and enterprise teams from the same core architecture. The assistant must treat groups, projects, goals, todos, skills, commands, connectors, model credentials, memories, artifacts, and knowledge bases as first-class durable entities.
The most important architectural decision is to keep the system multi-tenant-ready from day one. "From day one" applies to the data model, not the build order: every resource carries explicit ownership and scope columns so multi-tenancy is never blocked — but the governance engine (RBAC, deny-overrides-allow resolution, the config resolver) is built when multi-user, BYOK scoping, and tools make it real (roadmap v0.3), not in the first single-user release. This follows the "smallest single-loop harness first" principle: the schema anticipates global defaults, nested group/project configuration, user-owned credentials, per-chat overrides, per-run snapshots, and policy resolution, while the first shippable version (v0.1) is a minimal single-user Q&A loop.
The second major decision is to make every assistant response a durable worker run. The web request should not do the work directly. A user message should create a message record, enqueue a run, stream progress events, persist intermediate todo/artifact/tool state, and allow the UI or messaging channel to reconnect after refresh without losing progress.
The third major decision is to make the assistant wiki-centric. Chat is the interface, but a user's or organization's knowledge base should be the long-term memory substrate. Obsidian-like local Markdown vaults, Notion workspaces, Logseq-like graphs, local folders, Git repositories, document stores, and future enterprise knowledge systems should be normalized through a common Knowledge Space abstraction.
The fourth major decision is to support interactive artifacts as executable, shareable, versioned work products. Claude-style artifacts are a useful baseline, but this product should extend them with project association, GitHub linkage, sandbox execution, versioning, export formats, public/private sharing, provenance, and optional cloud/local runners.
OpenClaw-inspired personal agents. Current public descriptions of OpenClaw position it as a self-hosted/local autonomous assistant accessed mainly through messaging channels, with local history, external LLMs, skills, and real-world actions through tools and integrations. The strongest product lessons are: messaging-first agents feel natural; skills unlock extensibility; local deployment is attractive; but broad device, file, browser, shell, and credential access creates major security risks if not governed by policy, sandboxing, and approvals. Public reporting and research around OpenClaw-style skill ecosystems show why untrusted skills must be signed, scanned, capability-declared, sandboxed, and auditable. See source notes [S13], [S14], [S15], [S23].
Claude Code. Claude Code's current docs expose useful primitives: slash commands, skills loaded lazily from SKILL.md, nested/project/personal/enterprise skill locations, hooks, subagents, MCP, background tasks, worktrees, and shareable artifacts. Important lessons: commands should be first-class; skills should be packaged and lazily loaded; long-running tasks need visible state; artifacts should be versioned and shareable; permission rules and organization controls matter. See [S2], [S3], [S16].
OpenAI Codex. Codex docs show a modern coding-agent surface spanning CLI, app, IDE, web, GitHub/Slack/Linear integrations, MCP, permissions, hooks, AGENTS.md, sandboxing, workflows, skills, subagents, and app-server style session continuity. Important lessons: one agent backend should support multiple clients; local and remote execution modes should share the same session model; command-driven workflows are valuable for power users; sandboxes and approvals are mandatory for write/execute operations. See [S5], [S6].
MCP. MCP is now a central integration standard for connecting AI apps to external data, tools, prompts, and workflows. Its model of hosts, clients, servers, tools, resources, prompts, capability negotiation, progress, cancellation, and security/consent principles should be adopted directly. This product should be an MCP host and also expose selected project/assistant capabilities as MCP servers where useful. See [S1], [S17].
Open WebUI. Open WebUI provides a strong self-hosted model-agnostic baseline: Ollama and OpenAI-compatible providers, Docker deployment, plugins, tool calling, RAG, terminal/computer integrations, knowledge sync, and MCP-to-OpenAPI proxy tooling. Lessons: provider-agnostic architecture, local/offline operation, and Docker-first setup are table stakes. See [S7].
LibreChat. LibreChat is a self-hosted chat platform with MCP, agents, code interpreter, artifacts, custom endpoints, Docker, and flexible configuration. Lessons: compatibility with multiple provider APIs and the familiar ChatGPT-like UI lowers adoption friction; adding MCP and artifacts to chat UIs is increasingly expected. See [S8].
AnythingLLM. AnythingLLM emphasizes workspaces, multiple vector database backends, model routers, agents, custom skills, scheduled jobs, RAG, document agents, Gmail/Calendar/Outlook agents, Telegram channel support, and self-hosted/desktop/cloud options. Lessons: workspaces map well to projects/groups; model routing and vector backend abstraction should be first-class; scheduled jobs and channel integrations are valuable beyond chat. See [S9].
Dify. Dify's self-hosted Docker Compose deployment includes separate API, worker, worker beat, web, plugin daemon, vector DB, PostgreSQL, Redis, nginx, SSRF proxy, and sandbox containers. Dify's Knowledge model also provides a useful RAG baseline with knowledge bases, retrieval testing, metadata filtering, embedding configuration, and external knowledge sources. Lessons: split API and worker early; include plugin isolation; include sandbox and SSRF controls in the default deployment; make RAG testable, inspectable, and tunable. See [S10], [S11].
LobeHub. LobeHub's current docs emphasize agents as units of work, agent groups, projects, workspaces, personal memory, command menu, artifacts, messaging channels, many model providers, and MCP-compatible skills. Lessons: users increasingly expect agent teams, project/resource libraries, memory transparency, and model/provider freedom. See [S12].
Khoj. Khoj is a useful reference for a personal AI "second brain": it can chat over user files, search notes/documents using natural language, understand Markdown/PDF/plaintext/org-mode/Notion, integrate with Obsidian and Emacs, and self-host on consumer hardware. Lessons: a personal assistant should treat personal notes and documents as the core knowledge base, not as optional uploads. See [S18].
Hermes Agent and agent persistence lessons. Hermes Agent (Nous Research) and OpenClaw [S13] are single-user, local-first agents whose storage choices are instructive precisely because llame inverts them (multi-user, server, Postgres). Both right-size to SQLite (ACID, portability, rich queries, no ops burden) — the single-user analogue of our Postgres-first bet — and both keep append-only conversation transcripts plus a session id for resume (validating the run/chat event store) while separating raw episodic transcripts from curated semantic memory and procedural skills (validating the §20 memory layering). The cautionary lessons: (1) unbounded growth — Hermes documents that its event store accumulates forever and is being retrofitted with 30-day cleanup, so retention/partitioning must be designed into the event tables from the start; (2) files as source of truth — OpenClaw's Markdown-first model is excellent for one local user but disqualifying for a multi-user server (no concurrency, cross-user queries, or transactional integrity), so llame keeps the database as the system of record and treats Markdown vaults as imported Knowledge Spaces; (3) derived indexes must be rebuildable — OpenClaw's SQLite index tracks content hashes to skip re-indexing and regenerates from the files, so llame's embeddings/FTS are derived from the event log, never authoritative; (4) compaction as lineage — Hermes links compacted sessions parent/child so history stays auditable and rewindable; (5) scan-on-write + dedup for agent-written memory to blunt prompt-injection and exfiltration. See [S26], [S27].
Notion. Notion's API model distinguishes internal connections, OAuth public connections, and personal access tokens, each with different permission and content-access semantics. Lessons: connector credentials must support team-owned automation credentials, OAuth app installs, and user-owned tokens; content access must be explicit and inspectable. See [S19], [S20].
n8n. n8n is not primarily a chat assistant, but it is a strong reference for workflow execution, credentials, projects, sharing, user access, RBAC, SAML/OIDC, executions, and debugging. Lessons: agentic automation should borrow workflow-platform governance, not just chatbot UX. See [S21].
The names Hercules and Odysseus were searched as current/public personal AI assistants or agent platforms, but reliable public technical docs were not found under those exact names during this pass. They should be treated as unverified placeholders until specific repositories, docs, or vendor pages are provided. Do not infer architecture or capabilities for them without source material.
The product must enable:
- Self-hosted deployment through Docker Compose with sane defaults, health checks, migrations, backups, and safe upgrades.
- Multi-user operation from the ground up for individuals, households, teams, organizations, and enterprises.
- Nested groups such as
enterprise -> product -> team, with inherited settings, policies, connectors, skills, and RAG sources. - Project-centric collaboration with shareable workspaces, per-project connectors, MCP servers, knowledge sources, artifacts, GitHub repositories, sandboxes, and roles.
- Personal wiki-centric knowledge via a provider-neutral abstraction for Obsidian, Notion, Logseq, local folders, Git repos, and future systems.
- Integrated chat/project/wiki/artifact search using metadata, summaries, full text, embeddings, graph links, and optional reranking.
- Durable async runs where every user message is processed by a worker, not by the HTTP request thread.
- Refresh-safe progress where the UI can reconnect to an event stream and restore the current run state.
- Claude-style artifacts on steroids with interactivity, versioning, execution, GitHub linkage, sharing, and sandboxing.
- BYOK and OAuth credentials for LLM providers and external services, with no requirement for an instance-wide model provider.
- MCP-first integrations with per-scope capability governance.
- Third-party messaging interfaces such as Telegram, Discord, Slack, Matrix, Signal/WhatsApp where feasible, and email.
- Slash commands, goals, todos, and skills as durable agent-control primitives.
- Transparent memory and auditability so users can inspect what the assistant knows, why it did something, and which tools/data were used.
- Extensible microservice-ready architecture that starts simple but can split cleanly as usage grows.
The MVP should not try to:
- Train a proprietary foundation model.
- Build a public marketplace for untrusted executable skills before the security model is mature.
- Offer arbitrary unaudited shell access from chat.
- Implement every third-party connector directly; MCP and connector adapters should cover most integrations.
- Become a full replacement for GitHub, Notion, Obsidian, Slack, Google Drive, or n8n.
- Support enterprise compliance certifications on day one, though the data model should not block them.
- Support distributed multi-region active-active operation in the first release.
Durable state over transient prompt tricks. Todos, goals, memories, summaries, artifacts, project configs, skills, command invocations, tool calls, and approvals must be stored as structured data, not just hidden in chat text.
Policy before capability. A connector, MCP tool, skill, model, or sandbox capability must not be available merely because it is installed. It must be allowed by effective policy for the current user, group, project, chat, and run.
Deny overrides allow. When global or parent-group policy denies an action, lower scopes cannot re-enable it.
Config is inherited, resolved, and snapshotted. Every run should store the effective configuration snapshot used to execute it. This supports debugging, auditing, and reproducibility.
BYOK means truly user-owned. The instance should work even when the admin configures no LLM provider. Users, groups, or projects can supply credentials if policy allows.
Wiki is memory, not a side upload. Personal and group knowledge systems should become continuously indexed knowledge spaces with graph links, permissions, summaries, and retrieval.
Artifacts are work products. Artifacts should be versioned, linked to chats/projects/runs, searchable, exportable, shareable, and optionally executable.
Every long-running operation is resumable. The frontend and messaging channels must be clients of an event log, not holders of fragile in-memory state.
Instance
└── Root organization / household
└── Nested group
└── Nested subgroup
└── Project
└── Chat
└── Message
└── Run
A real enterprise example:
Acme Corp
└── Product
└── Platform
└── AI Infrastructure
└── Project: Internal AI Assistant
└── Chat: RAG Indexing Architecture
A family example:
Smith Household
└── Parents
└── Kids
└── Shared Home Projects
└── Project: Summer Trip Planning
The platform should support these scopes:
global
org_unit
group
project
user
chat
message
run
artifact
knowledge_space
connector
skill
command
A setting may exist at multiple scopes. The effective value is computed by the Config Resolver.
global defaults
→ root org settings
→ parent group settings, in path order
→ current group settings
→ project settings
→ user settings
→ chat settings
→ command/message/run overrides
→ policy removal of denied capabilities
For array-like settings such as enabled MCP servers, the merge strategy must be explicit:
merge_strategy:
providers: override_by_id
mcp_servers: additive_with_deny
skills: additive_with_deny
rag_sources: additive_with_deny
commands: additive_with_override
model_policy: most_restrictive
network_policy: most_restrictiveEvery run must store:
run_config_snapshot
- run_id
- user_id
- group_path
- project_id
- chat_id
- selected_model
- available_models
- provider_credential_refs
- enabled_connectors
- enabled_mcp_servers
- enabled_skills
- enabled_commands
- rag_sources
- artifact_policy
- sandbox_policy
- network_policy
- approval_policy
- retention_policy
- computed_at
- config_version_ids
- policy_version_ids
This snapshot is essential for auditing questions like "Why was this tool available?" or "Which knowledge sources were used?"
Users can be local accounts, SSO accounts, OAuth-linked accounts, or invited guests.
Required features:
- Local login for self-hosted setups.
- Optional OIDC/SAML for organizations.
- Multiple linked external identities per user.
- User-owned provider credentials.
- User-owned personal wiki sources.
- User-visible memory and profile settings.
- Per-user model preferences where policy permits.
- User data export.
org_units represent organizations, families, divisions, teams, departments, or arbitrary groups.
Required features:
- Arbitrary parent-child nesting.
- Path materialization for fast permission checks.
- Settings at each node.
- Memberships at any node.
- Inherited memberships and explicit local memberships.
- Owners/admins at each node.
- Group-scoped connectors, skills, RAG sources, model policies, budgets, retention, and audit settings.
MVP roles:
owner
admin
maintainer
member
viewer
guest
service_account
Role examples:
- Owner: can manage billing/license, root settings, all policies, and destructive operations.
- Admin: can manage groups, users, connectors, skills, and projects within scope.
- Maintainer: can configure project resources and project-specific connectors.
- Member: can use projects/chats and create artifacts subject to policy.
- Viewer: can read shared chats/artifacts but cannot run tools.
- Guest: limited project-specific collaborator.
- Service account: used by connectors and automations, never interactive by default.
Use a combined RBAC/ABAC model:
effect: allow | deny
action: "connector.invoke" | "artifact.publish" | "sandbox.execute" | "model.use"
resource_type: "mcp_server" | "provider" | "skill" | "artifact" | "project"
resource_id: optional
conditions:
project_id: optional
user_role_in_project: optional
network_zone: optional
max_cost_usd: optional
requires_approval: trueRules:
- Denies override allows.
- More specific policies override less specific policies only when they do not conflict with deny.
- Policies are versioned.
- Policy decisions are logged.
- High-risk actions require explicit approval even if generally allowed.
Actions requiring approvals may include:
- Shell execution.
- Browser/computer-use automation.
- Sending emails/messages.
- Mutating calendars.
- Writing to Notion/Obsidian/GitHub.
- Accessing sensitive files.
- Publishing artifacts externally.
- Using expensive models above budget.
- Invoking untrusted skills.
- Calling MCP tools marked destructive.
- Exfiltrating retrieved data to a remote provider.
Approval types:
never_allowed
always_ask
ask_once_per_run
ask_once_per_chat
ask_once_per_project
auto_allow_readonly
auto_allow_low_risk
admin_only
A Project is a shared workspace that contains:
- Chats.
- Goals and todos.
- Artifacts.
- Project documents.
- Knowledge sources.
- GitHub repositories.
- MCP servers.
- Skills and slash commands.
- Project memory.
- Members and roles.
- Secrets and credentials.
- Sandboxes.
- Run history.
- Audit events.
- Search indexes.
Projects can be private to a user, shared with a group, or shared with explicit users.
MVP should support:
personal
shared
team
organization
public_readonly
template
archived
Example:
project:
name: "Internal AI Assistant"
default_model: "user-preferred"
allowed_model_classes:
- fast
- reasoning
- local
rag:
enabled_sources:
- project_docs
- linked_chats
- linked_wiki_spaces
- github_repo_docs
retrieval_profile: "balanced"
connectors:
github:
enabled: true
credential_policy: "project_or_user"
notion:
enabled: true
mode: "read_only"
mcp_servers:
- github
- filesystem_project_readonly
- postgres_readonly
skills:
- spec-writer
- pr-reviewer
- release-notes
artifacts:
allow_interactive: true
allow_public_share: false
allow_sandbox_execution: true
sandbox:
runner: docker
network: restricted
max_cpu: 2
max_memory_mb: 2048
max_duration_seconds: 600Capabilities:
- Invite users.
- Share with nested group.
- Public read-only artifact/chats if enabled.
- Per-project guest access.
- Project templates.
- Fork project with optional knowledge/artifact copy.
- Export project bundle.
draft
active
paused
archived
deleted_pending_retention
purged
Archive should freeze new runs but preserve search and artifacts.
A chat belongs to a user or project and may be shared. It has:
- Title.
- Participants.
- Project linkage.
- Active goal.
- Chat summary.
- Linked knowledge sources.
- Linked artifacts.
- Linked todos.
- Retrieval profile.
- Model/provider preference.
- Privacy/retention policy.
Messages include:
message_id
chat_id
sender_type: user | assistant | system | tool | channel | automation
sender_id
content_blocks
attachments
command_invocation_id
created_at
edited_at
deleted_at
visibility
Content blocks should support:
text
markdown
image
audio
file
artifact_reference
todo_reference
tool_call_summary
approval_request
citation
code
structured_json
Each user message creates a run unless it is a purely local UI action. Runs are processed by workers.
Run statuses:
queued
resolving_config
retrieving_context
planning
waiting_for_approval
running_model
running_tool
running_sandbox
updating_artifact
summarizing
completed
failed
cancelled
expired
A run event stream should be append-only and replayable:
run_events
- run_id
- sequence
- event_type
- payload_json
- created_at
The event log is the durable source of truth and grows unbounded; design it partition-friendly by created_at so cold partitions can be archived (e.g. to object storage) instead of retrofitting cleanup later — the failure mode every local-first agent eventually hits ([S26]). Derived indexes (embeddings, FTS) are rebuildable from this log and are never authoritative ([S27]).
Event types:
run.created
run.started
config.resolved
retrieval.started
retrieval.hit
retrieval.completed
model.requested
model.delta
model.completed
tool.requested
tool.approval_required
tool.started
tool.stdout
tool.stderr
tool.completed
todo.created
todo.updated
artifact.created
artifact.updated
artifact.version_published
sandbox.started
sandbox.completed
summary.updated
run.completed
run.failed
The UI subscribes by cursor:
GET /api/runs/{run_id}/events?after_sequence=123
Accept: text/event-streamIf the page refreshes, it reads the latest run state and resumes from the last event sequence.
The API thread must only:
- Validate the request.
- Store the message.
- Create the run.
- Enqueue the run.
- Return the run ID.
Workers perform:
- Config resolution.
- Retrieval.
- Prompt/context assembly.
- Model calls.
- Tool loops.
- Todo updates.
- Artifact generation.
- Summarization.
- Index updates.
- Final message creation.
MVP uses pg-boss on PostgreSQL — SELECT … FOR UPDATE SKIP LOCKED for atomic dequeue, built-in scheduling, retries with backoff, and archiving (it can optionally use LISTEN/NOTIFY to lower wake-up latency; tuning is deferred to implementation). No Redis, no broker, no separate scheduler service; the queue lives in the same Postgres as everything else (§24.0). It sits behind an interface so it can be swapped for Redis + BullMQ (very high job throughput) or Temporal (durable long-horizon workflows) per §23.4 — only when a measured limit forces it.
Required queue features:
- At-least-once processing.
- Idempotency keys.
- Retry with exponential backoff.
- Dead-letter queue.
- Per-user/project concurrency limits.
- Priority lanes.
- Cancellation.
- Run timeout.
- Worker heartbeat.
A goal is a durable objective for a chat or project.
goals
- id
- scope_type: chat | project
- scope_id
- title
- description
- status: active | paused | completed | cancelled
- created_from_message_id
- active_run_id
- linked_todo_ids
- linked_artifact_ids
- created_by
- created_at
- updated_at
The /goal command should create or update the active goal. It can optionally generate initial todos.
Example:
/goal Write the self-hosted AI assistant specification and turn it into a Markdown artifact.
Expected behavior:
- Store the goal.
- Generate or update todos.
- Update chat/project summary.
- Start or modify the current run.
- Show the active goal in the chat header.
Todos are durable work-state objects, not prompt-only checklists.
todo_items
- id
- scope_type: chat | project | user | group
- scope_id
- title
- description
- status: todo | in_progress | blocked | done | cancelled
- priority
- owner_type: user | assistant | agent | group
- assignee_user_id
- source_message_id
- parent_todo_id
- dependency_ids
- acceptance_criteria
- linked_artifact_ids
- linked_run_ids
- linked_github_issue_id
- created_by
- updated_by
- created_at
- updated_at
Todo events:
todo_events
- todo_id
- event_type: created | updated | started | blocked | completed | reopened | linked_artifact | linked_commit
- actor_type
- actor_id
- message_id
- run_id
- payload_json
- created_at
The chat view should show:
- Active goal.
- Current run progress.
- Todo board or compact todo list.
- Which todo the assistant is working on.
- Blocked items and reasons.
- Linked artifacts and source messages.
The assistant can update todos during long work, but user-visible changes should be stored and reviewable.
Slash commands are parsed before the model call. Commands can be handled by:
internal handler
skill handler
MCP prompt/tool
workflow handler
project script
connector action
agent/subagent template
Command objects:
commands
- id
- name
- description
- scope_type
- scope_id
- manifest_json
- handler_type
- handler_ref
- required_permissions
- enabled
- created_at
- updated_at
Invocation log:
command_invocations
- id
- command_id
- user_id
- chat_id
- project_id
- message_id
- run_id
- args_json
- result_json
- created_at
MVP built-ins:
/goal Set or update active chat/project goal.
/todo Create, list, update, complete, or block todos.
/search Search chats, messages, summaries, wiki, project docs, and artifacts.
/wiki Query, mount, sync, or inspect knowledge spaces.
/project Switch, inspect, configure, share, or archive project.
/connect Connect provider, MCP server, OAuth integration, or local source.
/model Select model, provider, effort profile, or routing mode.
/skill Install, enable, disable, inspect, or run skills.
/artifact Create, open, fork, export, publish, or execute artifacts.
/run Start, inspect, cancel, retry, or resume a run.
/memory Save, forget, inspect, or scope memory.
/summarize Summarize chat, project, artifact, source, or timeframe.
/export Export chat/project/artifacts/memory.
/policy Explain allowed/denied capability decisions.
/debug Show run trace, config snapshot, retrieval hits, and tool calls.
name: goal
description: Set the active goal for this chat or project.
scope:
- chat
- project
args_schema:
type: object
properties:
text:
type: string
mode:
type: string
enum: [replace, append, refine]
permissions:
- goals.write
handler:
type: internal
ref: goal.set
autocomplete:
argument_hint: "<goal>"Command precedence:
global built-ins
→ org/group commands
→ project commands
→ user commands
→ chat temporary commands
→ skill-provided commands
Name collisions must be explicit. Recommended syntax for namespacing:
/skill-name
/plugin-name:command
/project-path:command
A skill is a versioned package that extends assistant behavior. It can contain instructions, supporting files, tool adapters, slash commands, schemas, RAG material, artifact templates, hooks, and evals.
The package follows the Agent Skills standard ([S2], [S22]): a directory whose only required file is SKILL.md (YAML frontmatter + Markdown instructions), with optional scripts/, references/, and assets/ subfolders. There is no separate skill.yaml — all author-declared metadata lives in the SKILL.md frontmatter (§12.3). This matches both Anthropic Agent Skills and OpenClaw skills, which use a single SKILL.md. Folders beyond the standard three (commands/, evals/, policies/) are this platform's documented extensions, not core.
my-skill/
├── SKILL.md # required: frontmatter + instructions (the only mandatory file)
├── scripts/ # optional (standard): executable code run in the sandbox
├── references/ # optional (standard): docs loaded on demand
├── assets/ # optional (standard): templates, schemas, images
├── commands/ # optional (platform extension): slash commands the skill provides
├── evals/ # optional (platform extension): regression evals
├── policies/ # optional (platform extension): default approval/policy hints
└── README.md
SKILL.md should contain concise human-readable instructions. It should not be the only security boundary.
Example:
---
name: pr-reviewer
description: >-
Reviews GitHub pull requests for correctness, security, and maintainability.
Use when a user asks to review a PR, inspect a diff, or prepare merge feedback.
---
Use the GitHub connector to read the PR diff.
Summarize high-risk issues first.
Never push commits unless the user explicitly approves write access.The "when to use" guidance lives inside description (the standard's idiomatic place); any platform-specific flags go under metadata (§12.3), not new top-level keys.
All author-declared metadata lives in the SKILL.md frontmatter — there is no second file. Standard fields (name, description, license, allowed-tools, metadata, …) follow the Agent Skills spec ([S22]); platform-specific needs are namespaced under metadata. The block below is illustrative of shape and intent — exact frontmatter field names and value constraints are pinned to the spec at the skill-system implementation stage, not in this product spec.
---
name: github-pr-review
description: >-
Reviews GitHub pull requests for correctness, security, and maintainability.
Use when a user asks to review a PR, inspect a diff, or prepare merge feedback.
license: Apache-2.0
allowed-tools:
- Read
- Bash(git:*)
metadata:
version: 1.2.0
# author-DECLARED capability requests (what the skill asks for; granted only if policy allows)
requested_capabilities:
network_outbound:
- api.github.com
filesystem: none
sandbox:
required: true
network: restricted
required_model_capabilities:
- code_reasoning
commands:
- /review-pr
mcp_servers:
- github
---Critical separation: author-declared vs platform-assigned. The frontmatter only carries what the author requests. Trust, signatures, provenance, and scan results are assigned by the registry/install layer, never read from the package — a malicious author would simply write trust: verified. The OpenClaw / ClawHub incidents ([S14], [S15], [S23]) are the cautionary case: thousands of community skills, ranking manipulation, infostealers in SKILL.md instructions, and no effective sandbox. The defenses that worked were registry-side (VirusTotal/ClawScan scanning, publisher-provenance audits, Skill Cards) plus per-session sandboxing — not self-declared trust.
These platform-assigned fields live in the data model (§25.5: skills.signature, skills.trust_level, skill_installations), populated at publish/install time:
signature # computed and verified by the registry, not the author
provenance # source repo, publisher identity, scan reports
trust_level # assigned via admin review (§12.5), never from frontmatter
builtin
instance-installed
group-installed
project-installed
user-installed
chat-installed
temporary-run
builtin
verified
admin-approved
user-installed
local-dev
untrusted
blocked
Trust level is assigned by the registry and admin review, stored on skills.trust_level (§25.5), and is never read from package frontmatter. MVP should allow builtin and admin-approved skills. User-installed executable skills should be post-MVP unless sandboxing and review are mature.
Skills should not be blindly injected into every prompt. Use a Skill Router:
message
→ command parser
→ intent classifier
→ candidate skill search
→ policy check
→ capability check
→ minimal skill context injection
→ run
The model should receive only:
- Skill name.
- Short description.
- Invocation conditions.
- Relevant instructions.
- Required capabilities.
- Referenced supporting files only when needed.
Required:
- Signed packages for non-local skills.
- Hash pinning.
- Provenance metadata.
- Static scanning.
- Secret scanning.
- Capability declarations.
- Network allowlists.
- Filesystem allowlists.
- Admin review workflow.
- Runtime sandboxing.
- Audit logs for every skill invocation.
- Prompt-injection-aware guidance provenance.
- A kill switch for compromised skills.
The assistant acts as an MCP host. It manages MCP clients connected to MCP servers.
It should support:
stdio MCP servers
HTTP/SSE MCP servers
streamable HTTP MCP servers
remote OAuth MCP servers
containerized MCP servers
MCP-to-OpenAPI proxy mode
MCP servers can be configured at:
global
org/group
project
user
chat
temporary run
Each MCP server declaration includes:
id: github-project
transport: streamable_http
url: "https://..."
scope: project
credential_ref: "project:github-installation"
capabilities:
tools:
- pull_requests.read
- issues.write
resources:
- repo.contents
policy:
require_approval_for:
- pull_requests.write
- issues.write
deny:
- repo.adminNot every integration will be MCP-native. Use a Connector Service that exposes a normalized internal interface:
Connector
- provider type: github | notion | google_drive | slack | telegram | local_fs | custom
- auth mode: oauth | api_key | pat | instance_secret | service_account | local
- capabilities
- resources
- actions
- webhooks
- sync jobs
- rate limits
- audit behavior
Each connector can optionally expose:
- MCP server.
- REST adapter.
- Webhook receiver.
- Sync indexer.
- OAuth app.
- UI settings panel.
Credential precedence must be explicit:
message/run explicit credential
→ chat credential
→ project credential
→ user credential
→ group credential
→ instance credential
Policy can restrict whether user credentials may be used in a project or whether project credentials may be used by a user.
Every tool should be classified:
read_only
write_low_risk
write_high_risk
execute_code
external_send
financial_or_sensitive
admin
The runtime should use this classification to trigger approvals and sandbox restrictions.
Providers should be normalized as:
provider_accounts
- id
- owner_scope_type
- owner_scope_id
- provider_type
- display_name
- auth_mode
- base_url
- models_cache
- enabled
- created_at
Provider adapters are a small set. Most "providers" are not distinct adapters — they are OpenAI-compatible endpoints distinguished only by base_url, so they ship as presets of openai_compatible rather than separate code paths.
openai_compatible # OpenAI + presets: openrouter, groq, together, mistral, azure_openai, vllm, llama_cpp, lm_studio
anthropic
google_gemini
aws_bedrock
ollama # local
custom_http
Implemented through the AI SDK v5 + LangChain provider packages already present in the repo (@langchain/openai, @langchain/anthropic, @langchain/xai, @langchain/community, Ollama via community). New OpenAI-compatible vendors are added as configuration (a preset + base_url), not new adapters.
Credentials are encrypted and scoped:
credentials
- id
- owner_scope_type
- owner_scope_id
- provider_account_id
- secret_type: api_key | oauth_token | pat | service_account | local_socket
- encrypted_payload
- key_version
- expires_at
- rotation_state
- created_by
- last_used_at
The instance should boot with no model provider. Users can add their own provider credentials if policy allows. For a family or organization, admins can configure group-level or project-level providers.
The Model Router selects a model based on:
- User preference.
- Project policy.
- Required modality.
- Required tool support.
- Reasoning level.
- Context length.
- Cost budget.
- Latency target.
- Data locality requirement.
- Provider health.
- Fallback policy.
Example routing profile:
profile: balanced
rules:
- when: { task: "simple_chat" }
prefer: ["local.fast", "cloud.fast"]
- when: { task: "code_reasoning" }
prefer: ["cloud.reasoning", "local.code"]
- when: { data_classification: "sensitive" }
require: ["local_or_approved_private_provider"]
- when: { monthly_budget_remaining_usd_lt: 5 }
prefer: ["local"]Track:
- Tokens in/out.
- Embedding tokens.
- Tool cost.
- Sandbox runtime cost.
- Provider cost.
- User/project/group monthly budgets.
- Warnings and hard caps.
A Knowledge Space is a mounted source of truth.
Supported MVP sources:
local_folder_markdown
obsidian_vault
notion_workspace
notion_page_tree
git_repository_docs
uploaded_project_documents
chat_history
artifact_library
Post-MVP sources:
logseq_graph
google_drive
confluence
slack_history
email_archive
linear_jira
readwise
browser_bookmarks
rss_feeds
knowledge_spaces
- id
- owner_scope_type
- owner_scope_id
- source_type
- display_name
- connector_id
- root_ref
- sync_mode: manual | scheduled | webhook | file_watch
- write_policy: read_only | append_only | bidirectional | disabled
- indexing_policy
- permissions_policy
- created_at
- updated_at
Normalize each source into:
documents
- id
- knowledge_space_id
- external_id
- canonical_uri
- title
- document_type
- mime_type
- content_hash
- metadata_json
- graph_json
- permission_fingerprint
- last_source_updated_at
- last_indexed_at
Chunks:
document_chunks
- id
- document_id
- chunk_index
- heading_path
- content_text
- content_tokens
- metadata_json
- embedding_id
- content_hash
Graph relations:
knowledge_edges
- from_document_id
- to_document_id
- relation_type: backlink | wikilink | tag | parent | child | citation | attachment | alias
- metadata_json
The Obsidian-style adapter should support:
- Local filesystem vault.
- Markdown files.
- YAML frontmatter.
- Wikilinks
[[Page]]. - Tags.
- Attachments.
- Daily notes.
- Canvas files where possible.
- Git-backed sync if the vault is in a repository.
- Read-only MVP; append/update post-MVP.
The Notion adapter should support:
- OAuth public connections for multi-user installs.
- Internal connections for team-owned automations.
- Personal access tokens for user-owned scripts.
- Page and database reads.
- Page tree traversal.
- Comments where permitted.
- Webhook subscriptions where configured.
- Explicit content access checks.
The assistant should not store all memories only in a hidden memory database. Instead, it should support memory destinations:
assistant_private_memory
user_visible_memory
project_memory
wiki_append_note
daily_note_append
notion_page_append
A user can configure:
memory_policy:
default_destination: user_visible_memory
allow_wiki_writes: true
wiki_write_requires_approval: true
daily_note_target: "Obsidian/Daily Notes/{date}.md"Retrieval should account for:
- Full-text chunk match.
- Vector semantic match.
- Title and alias match.
- Tags.
- Frontmatter metadata.
- Backlink graph proximity.
- Recency.
- User/project permissions.
- Pinned sources.
- Manual user selections.
Users should be able to ask:
- "Find the chat where we discussed the worker architecture."
- "What did we decide about using pgvector vs Qdrant?"
- "Search my Obsidian vault for notes related to the MCP security model."
- "Find artifacts created for the AI assistant project."
- "Use past chats and project docs to answer this."
Index:
chat titles
chat summaries
message content
message attachments
run traces, selectively
tool call summaries
todos and goals
artifact titles and content
artifact versions
project docs
wiki documents
GitHub issue/PR summaries
connector-synced documents
Use multiple summary layers:
message-level extracted facts
rolling chat summary
topic summary
goal summary
project summary
artifact summary
knowledge-space summary
user memory summary
Each summary should be timestamped, versioned, and linked to source messages/documents.
Recommended pipeline:
1. Permission filter
2. Scope filter
3. Query understanding
4. Exact/fuzzy title search
5. Metadata search
6. Full-text lexical search
7. Vector search over chunks/summaries/artifacts/wiki
8. Graph expansion
9. Reranking
10. Diversity filtering
11. Citation/source assembly
12. Answer synthesis or result display
MVP uses PostgreSQL for everything: relational metadata, full-text search (FTS), and embeddings (pgvector). One datastore, one backup, hybrid retrieval in a single query. No separate lexical or vector engine in MVP.
Scale path (the §23.4 swaps, applied only on a measured limit): pgvector → Qdrant for vector; Postgres FTS → Meilisearch/OpenSearch for lexical; add a dedicated re-ranker service. These swap behind the Search/RAG service interface (§22.11) without changing callers.
Every RAG answer should expose:
- Sources used.
- Source scope.
- Retrieval query.
- Retrieval profile.
- Confidence/coverage.
- Date indexed.
- Permission boundary.
- Option to open the original message/document/artifact.
For untrusted retrieved content:
- Mark content as untrusted.
- Strip or isolate instructions embedded in retrieved documents.
- Never allow retrieved content to override system/developer/user instructions.
- Quote sources as data, not commands.
- Require approval before tool actions suggested by retrieved content.
- Log retrieval-to-action chains.
Artifacts are durable, versioned work products.
artifacts
- id
- project_id
- chat_id
- owner_user_id
- title
- artifact_type
- current_version_id
- visibility
- execution_policy
- source_run_id
- source_message_id
- github_repo_id
- created_at
- updated_at
Versions:
artifact_versions
- id
- artifact_id
- version_number
- content_ref
- content_hash
- metadata_json
- created_by
- source_run_id
- created_at
MVP:
markdown_document
html_page
mermaid_diagram
code_snippet
react_component
json_schema
api_spec
checklist
Near-term:
docx_document
pdf_export
spreadsheet
notebook
dashboard
interactive_form
canvas
whiteboard
diagram_pack
test_report
release_notes
Later:
full_stack_app
hosted_microapp
data_app
simulation
workflow
agent_team
Interactive artifacts should support:
- Preview in chat side panel.
- Full-screen view.
- Version history.
- Diff between versions.
- Forking.
- Export.
- Copy as prompt.
- Linking to todos/goals.
- Linking to GitHub commits/PRs.
- Optional sandbox execution.
- Share links with policy controls.
- Public sharing only if enabled by policy.
static_preview # MVP
browser_sandbox # MVP (sandboxed iframe for HTML/React artifacts)
docker_sandbox # MVP (project-trusted code)
local_runner # roadmap
cloud_runner # roadmap
kubernetes_runner # roadmap (scale swap, §23.4)
firecracker_microvm # roadmap (scale swap, §23.4)
MVP: static preview, sandboxed-iframe browser preview, and Docker sandbox for project-trusted code. The remaining runners are the §23.4 sandbox scale path, added only when hostile multi-tenant code execution demands stronger isolation.
Sandbox requirements:
- Resource limits.
- Network policy.
- Filesystem policy.
- Secret injection only by explicit permission.
- No default access to host Docker socket.
- Logs captured as run events.
- Artifact provenance preserved.
- Rebuild from versioned source.
Artifacts can be associated with a GitHub repository:
- Artifact source stored in repo.
- Artifact generated from branch/worktree.
- Artifact linked to issue/PR.
- Artifact changes committed by assistant with approval.
- Artifact preview posted to PR.
- Artifact version maps to commit SHA.
- Project can define repo-level artifact directories.
Example:
artifact:
id: arch-spec
repo: acme/assistant
branch: feature/spec-artifact
path: docs/assistant-feature-spec.md
linked_pr: 123
publish_policy: org_onlyProjects can link one or more repositories:
repositories
- id
- provider: github | gitlab | gitea
- owner
- name
- default_branch
- installation_id
- credential_policy
- project_id
MVP:
- Read repo files.
- Search code.
- Read issues/PRs.
- Read diffs.
- Link chats/artifacts to issues/PRs.
- Create branches.
- Propose patches.
- Open PR with approval.
Post-MVP:
- Worktree-based parallel agents.
- CI-aware repair loops.
- Code review comments.
- Security scanning integration.
- Release automation.
- Multi-repo dependency graph.
- Local devcontainer integration.
Read operations can be auto-approved when project policy allows. Write operations require explicit approval unless an admin configures a trusted automation.
The Channel Gateway normalizes external messages into internal chat messages.
Supported MVP channels:
web app
telegram
discord
slack
email inbound
Future channels:
matrix
signal
whatsapp
sms
imessage bridge
teams
mattermost
zulip
A user must explicitly link external channel identities.
channel_accounts
- id
- user_id
- channel_type
- external_user_id
- external_username
- verified_at
- created_at
Messages route to:
- A personal default chat.
- A project chat by command.
- A channel-specific project.
- A thread-mapped chat.
- A bot DM chat.
- A group chat with shared participants.
Example:
Telegram DM → user's personal assistant
Discord server channel #ai-assistant → project chat
Slack thread → mapped chat thread
Email subject thread → project inbox chat
Channels should support:
- Streaming or periodic progress updates.
- Compact todo updates.
- Approval buttons where supported.
- Artifact links.
- File attachments.
- Voice note transcription where supported.
/project,/goal,/todo,/search,/run cancel.
- Do not expose admin commands in untrusted channels.
- Require identity linking.
- Per-channel rate limits.
- Per-channel capability policy.
- Optional "read-only channel mode."
- Avoid leaking private project names across channels.
- Validate webhooks.
- Store channel message IDs for audit and replies.
profile_memory
preference_memory
project_memory
relationship_memory
task_memory
decision_memory
wiki_memory
ephemeral_chat_memory
Every durable memory should be inspectable unless explicitly system-internal.
Memory controls:
- Save memory.
- Edit memory.
- Forget memory.
- Move memory to wiki.
- Scope memory to project/group/user.
- Disable memory for chat.
- Expire memory after duration.
- Show memory used in response.
The assistant should not silently store sensitive or high-impact memories. Use configurable thresholds:
memory_write_policy:
low_risk_preferences: auto_save_with_notification
project_decisions: ask
personal_sensitive: ask
credentials: never
third_party_personal_info: ask_or_neverMemory written by the agent (especially to wiki/notes) is scanned on write and deduplicated before acceptance — screen for prompt-injection, credential exfiltration, and invisible-Unicode payloads, and reject exact duplicates — the safeguard Hermes Agent applies to every memory entry ([S26]). Keep the layers distinct, not one bag: raw episodic events (the run/chat log, §9.4), curated semantic memory (this section), and procedural skills (§12) are separate stores ([S26], [S27]).
Memory retrieval should be included in the same multi-stage retrieval pipeline, but marked separately from document/chat sources.
flowchart TD
Web[Web App / PWA] --> API[API Gateway]
Mobile[Mobile / Desktop Shell] --> API
Channels[Telegram / Discord / Slack / Email] --> ChannelGateway[Channel Gateway]
ChannelGateway --> API
API --> Auth[Identity & Auth Service]
API --> Policy[Policy Service]
API --> Config[Config Resolver]
API --> Chat[Chat & Project Service]
API --> Queue[Run Queue]
Queue --> Workers[Assistant Workers]
Workers --> Config
Workers --> Policy
Workers --> Retrieval[Search & RAG Service]
Workers --> Model[Model Gateway / Router]
Workers --> MCP[MCP & Connector Manager]
Workers --> Skills[Skill Registry & Runtime]
Workers --> Artifacts[Artifact Service]
Workers --> Sandbox[Sandbox Runner]
Workers --> Events[Run Event Store]
Retrieval --> Pg[(PostgreSQL + pgvector + FTS)]
Artifacts --> ObjectStore[(Filesystem / S3)]
Chat --> Pg
Events --> Pg
Config --> Pg
Policy --> Pg
MCP --> Vault[Credential Vault]
Model --> Vault
Sandbox --> ObjectStore
API --> Events
Web --> Events
sequenceDiagram
participant U as User
participant UI as Web/Channel UI
participant API as API Gateway
participant Q as Queue
participant W as Worker
participant C as Config Resolver
participant R as Retrieval
participant M as Model Gateway
participant T as MCP/Tools
participant E as Event Store
U->>UI: Send message
UI->>API: POST message
API->>E: Store message + run.created
API->>Q: Enqueue run
API-->>UI: run_id
UI->>E: Subscribe run events
Q->>W: Start run
W->>E: run.started
W->>C: Resolve effective config
C-->>W: Config snapshot
W->>E: config.resolved
W->>R: Retrieve chat/project/wiki/artifact context
R-->>W: Ranked sources
W->>M: Model request
M-->>W: Tool call or text
W->>T: Invoke approved tool
T-->>W: Tool result
W->>M: Continue model loop
W->>E: model deltas / todo updates / artifact updates
W->>E: run.completed
UI-->>U: Replayed + live progress
flowchart LR
Global[Global Config] --> Resolver
Org[Org/Group Ancestors] --> Resolver
Project[Project Config] --> Resolver
User[User Config] --> Resolver
Chat[Chat Config] --> Resolver
Runtime[Run Overrides] --> Resolver
Policies[Allow/Deny Policies] --> Resolver
Resolver --> Snapshot[Effective Config Snapshot]
flowchart TD
Sources[Wiki / Notion / Docs / Chats / Artifacts / GitHub] --> Sync[Sync Workers]
Sync --> Normalize[Normalize Documents]
Normalize --> Permissions[Permission Fingerprint]
Normalize --> Chunk[Chunk + Extract Metadata]
Chunk --> Summaries[Summarize]
Chunk --> Embed[Generate Embeddings]
Chunk --> TextIndex[Lexical Index]
Embed --> VectorIndex[Vector Index]
Summaries --> VectorIndex
Permissions --> Search[Search Service]
TextIndex --> Search
VectorIndex --> Search
flowchart TD
Run[Assistant Run] --> Artifact[Artifact Service]
Artifact --> Version[Create Version]
Version --> Store[Object Store]
Artifact --> Policy[Execution Policy Check]
Policy --> Sandbox[Sandbox Runner]
Sandbox --> Logs[Run Events]
Sandbox --> Preview[Preview URL]
Version --> Git[Optional Git Commit / PR]
apps/api (NestJS) is the sole owner of the database — schema, migrations, all DB access, authentication, RLS, and domain logic; the database is a private implementation detail of apps/api that no other process connects to. It exposes two independently-versioned HTTP surfaces:
/auth/v1/*— authentication & session management (own version line — different security posture, rate limits, unauthenticated entry points)./api/v1/*— domain resources (chats, messages, models, …), each requiring a valid session.
Clients are thin and call the API directly. apps/web (Next.js) and any future apps/mobile are UI clients with no database, ORM, or auth adapter, and are not a request proxy/BFF — the browser calls apps/api directly at a configurable API_URL.
API contract — code-first OpenAPI. Every endpoint takes a typed, validated request DTO (fail-closed: unknown/invalid input rejected) and an explicit response type, from which a single openapi.json is derived. The emitted spec — not a hand-written contract — is the authoritative description of the surface; thin clients are typed off it, so contract drift is a build/type error, not a runtime fault. Design the surface RESTfully, not RPC — resources + standard verbs (GET/POST/PATCH/DELETE), not verb-y handles (/chats/:id/rename); the contract is a long-lived multi-client surface, and ad-hoc verbs become permanent debt. (Generated SDKs and non-TS clients are deferred until the surface is stable and a second consumer exists.)
Transport, client IP & deployment. Default deployment is three services (web · api · postgres) with the browser hitting api directly; because api terminates the connection it reads the real client IP from its own socket (reliable rate-limiting and IP hardening, no proxy). A reverse-proxy / same-origin mode is opt-in (API_URL=/api; api trusts X-Forwarded-For only when TRUST_PROXY is set). Chat streaming uses SSE (proxies cleanly; WebSockets do not).
Sessions & cookies. Revocable server-side sessions with an opaque token hashed at rest — not a stateless JWT, so they can be revoked (by row deletion) and rotated (on privilege/credential change). Transport is an HttpOnly cookie for web (set by api) and Authorization: Bearer for native clients, both resolving to the same session lookup. api runs as a same-site subdomain with a parent-domain cookie so it is readable by web's proxy (proxy.ts); an Origin allowlist + SameSite=Lax cover CSRF without a separate token (a fully different api domain would need SameSite=None + a CSRF token). /auth/v1 models sessions as a resource (login/register/me, plus session list/revoke). Grounded in Supabase / Ory / Okta practice and the OWASP cheat sheets.
How apps/web knows the auth state (layered). apps/web never reads the (HttpOnly) session — it asks api, in three layers: (1) proxy.ts (Next 16's rename of middleware.ts) does an optimistic cookie-presence check for a pre-render redirect (fast; not the security boundary); (2) the api guard is authoritative, deriving userId only from the validated session, never client input; (3) a client 401 interceptor catches mid-session expiry/revocation. GET /auth/v1/me is the source of truth for rendered auth state.
A thin client of apps/api (§22.0) — a UI/SPA with no database, ORM, or auth adapter; it calls the API directly at API_URL. Responsibilities:
- Chat UI.
- Project UI.
- Admin/group UI.
- Artifact viewer/editor.
- Search UI.
- Todo/goal panel.
- Command palette.
- OAuth/connectors UI.
- Run progress streaming.
- Approval prompts.
Suggested stack:
- Next.js or React SPA.
- TanStack Query or equivalent.
- SSE/WebSocket client.
- Monaco editor for code artifacts.
- Markdown/Mermaid rendering.
- Sandboxed iframe for artifacts.
Responsibilities:
- Authenticated REST/GraphQL API.
- Request validation.
- Message creation.
- Run enqueue.
- Event stream endpoints.
- Admin APIs.
- Webhook ingress routing.
- Rate limiting.
Responsibilities:
- Users.
- Sessions.
- OAuth/OIDC/SAML.
- Group tree.
- Memberships.
- Invitations.
- Service accounts.
Responsibilities:
- RBAC/ABAC decisions.
- Capability checks.
- Approval requirements.
- Deny override enforcement.
- Policy versioning.
- Policy explanation.
Responsibilities:
- Merge configuration across scopes.
- Resolve credentials by policy.
- Resolve model/provider.
- Resolve enabled skills/connectors/RAG.
- Store run config snapshots.
- Explain effective configuration.
Responsibilities:
- Encrypt secrets.
- Rotate keys.
- Store OAuth tokens.
- Refresh OAuth tokens.
- Scope credentials.
- Audit secret access.
- Optional integration with HashiCorp Vault, cloud KMS, or age/sops.
Responsibilities:
- Projects.
- Chats.
- Messages.
- Attachments.
- Goals/todos.
- Sharing.
- Summaries.
- Retention.
Responsibilities:
- Run queue.
- Worker scheduling.
- Idempotency.
- Cancellation.
- Retry.
- Tool loop.
- Model loop.
- Progress events.
- Run finalization.
Responsibilities:
- Provider abstraction.
- Model routing.
- Streaming.
- Token/cost accounting.
- Retry/fallback.
- Local model support.
- Safety/policy filtering.
- Structured output validation.
Implemented with the Vercel AI SDK v5 (streaming, tool calls, structured output via zod) and LangChain provider packages. The agent tool/model loop and any multi-agent/subagent work (§32.2) run on LangGraph (langgraph-supervisor), with run state persisted to the Postgres run-event store (§9.4) rather than held in worker memory.
Responsibilities:
- MCP client lifecycle.
- MCP server registry.
- Connector adapters.
- Webhook handling.
- Sync jobs.
- Tool capability metadata.
- Tool invocation auditing.
- Connector rate limits.
Responsibilities:
- Ingestion.
- Chunking.
- Embeddings.
- Lexical indexing.
- Vector indexing.
- Hybrid retrieval.
- Reranking.
- Citations.
- Retrieval evals.
- Permission filtering.
Responsibilities:
- Skill packages.
- Installations.
- Versions.
- Signatures.
- Trust levels.
- Lazy skill selection.
- Skill-provided commands.
- Skill-scoped policies.
- Skill audit logs.
Responsibilities:
- Artifact metadata.
- Versioning.
- Object storage.
- Preview rendering.
- Sharing.
- Exports.
- GitHub linkage.
- Artifact search indexing.
Responsibilities:
- Execute artifacts/tools safely.
- Docker-based MVP.
- Resource limits.
- Filesystem mounts.
- Network policy.
- Secret injection.
- Log capture.
- Cleanup.
Responsibilities:
- Channel updates.
- Email notifications.
- Web push.
- Run completion notifications.
- Approval reminders.
- Scheduled task triggers.
Responsibilities:
- Audit events.
- Run traces.
- Tool traces.
- Retrieval traces.
- Cost metrics.
- Error reporting.
- Admin dashboards.
- Exportable logs.
The stack is TypeScript end-to-end, matching the existing llame monorepo. There is no second backend language. This resolves Open Question 1 (§33): the backend is TypeScript/NestJS, already shipping.
Monorepo: pnpm + Turborepo, Node >= 20, TypeScript 5.7
Web: Next.js 16 (App Router) + React 19
Auth: revocable server-side sessions in apps/api (§22.0); apps/web is a thin client
Web data: TanStack Query, ky
UI: shadcn/ui (@workspace/ui), Tailwind, framer-motion
API: NestJS 11
ORM/DB: Drizzle ORM + postgres.js over PostgreSQL; drizzle-kit migrations
Agent/model: Vercel AI SDK v5 — single-model streaming chat loop in apps/api (§9.5); apps/web consumes the stream via the AI SDK transport
Schemas: zod (+ zod-to-json-schema for tool/structured output)
Observability: Sentry + pino (structured logs)
Worker: dedicated NestJS process (run executor) — never the HTTP request thread (§9.5)
Run runtime: single-model AI SDK loop (a multi-step/agent graph only when evals justify it); run state persisted to the Postgres run-event store (§9.4)
Queue+sched: pg-boss on PostgreSQL (polling-based queue + scheduler) — no Redis, no separate broker (§24.0)
Object store: local filesystem volume behind an S3-compatible interface (§24.0); MinIO/S3 is a scale swap
Sandbox: Docker with restricted profiles (§28.6)
Reverse proxy: Caddy (automatic TLS)
Tracing: OpenTelemetry -> Prometheus/Grafana/Loki (optional `observability` profile)
Deployment: Docker Compose
The API and worker are separate Node processes even though they share one language and one repo. Unifying the language must not collapse the API/worker boundary that §9.5 makes a durability requirement.
- TypeScript is the only language for web, API, worker, provider adapters, MCP clients, and connectors.
- Python is permitted only as an isolated, optional sidecar for a specific document-parsing or embedding library that has no viable JS/API path — and an API/JS route is preferred first.
- Go and Rust are out of scope. Reach for a scale swap (§23.4) before reaching for a new language.
Skill and plugin authors are not bound to TypeScript: skills are prompt-first (SKILL.md, §12); when a skill executes code it runs in the sandbox, decoupled from the host services and their language.
Each MVP component sits behind an interface so it can be replaced without touching callers. These are swaps triggered by a measured limit, not a menu — ship the MVP default, swap only when a metric forces it.
Queue: pg-boss/Postgres -> Redis+BullMQ (very high job throughput) or Temporal (durable workflows)
Cache: Postgres UNLOGGED tables -> Redis (high-QPS hot cache, §24.0)
Vector: pgvector -> Qdrant (embedding volume/latency outgrows Postgres)
Lexical: Postgres FTS -> Meilisearch / OpenSearch (large-corpus search UX / scale)
Objects: filesystem volume -> MinIO / S3 (large binaries, multi-node, CDN)
Sandbox: Docker -> Firecracker / Kata / K8s jobs (hostile multi-tenant code exec)
Secrets: app-level encryption -> HashiCorp Vault / cloud KMS
Database: single Postgres -> Postgres HA
The default deployment runs one stateful service: PostgreSQL. Most "you need a separate database for X" needs are met by a Postgres feature or extension, which for a self-hosted, single-operator product is a decisive operational win — one thing to back up, tune, monitor, and upgrade ([S24]).
| Need | Typical separate service | Postgres equivalent |
|---|---|---|
| Job / run queue | Redis + BullMQ | SELECT … FOR UPDATE SKIP LOCKED via pg-boss (§9.6) |
| Pub/sub & SSE wake-ups | Redis pub/sub | LISTEN / NOTIFY (durable source is the run-event table, §9.4) |
| Scheduled runs | separate scheduler / Celery | pg-boss scheduling (or pg_cron) — no extra service |
| Short-lived locks | Redis locks | advisory locks (pg_advisory_lock) |
| Cache / counters | Redis | UNLOGGED tables + PgBouncer pooling |
| Sessions | Redis | DB-backed (NextAuth Drizzle adapter, already used) |
| Vector search | Pinecone / Qdrant | pgvector (+ pgvectorscale at scale) |
| Full-text search | Elasticsearch / Meilisearch | tsvector + pg_trgm (BM25 via pg_search if needed) |
| Documents / JSON | MongoDB | JSONB + GIN indexes |
Required extensions stay minimal: pgvector plus pg_trgm (a contrib module that ships with Postgres; enable with CREATE EXTENSION). pg-boss needs no extension. This removes Redis, BullMQ, and a separate scheduler service from the MVP entirely.
Hard boundaries — do not force these into Postgres:
- Binary / large object storage. Text artifact content (Markdown, HTML, code) lives directly in
artifact_versions— no object store needed for MVP. Binary files, exports, parsed-doc blobs, large logs, and backups go to a local filesystem volume behind an S3-compatible interface, not into Postgres (blobs bloat the DB and wreck backup/restore). The "Postgres for everything" source deliberately omits blob storage; MinIO/S3 is the scale swap (§23.4), not the MVP default. - High-QPS hot cache at very large scale.
UNLOGGEDcache tables and a busySKIP LOCKEDqueue add write/vacuum pressure, andLISTEN/NOTIFYneeds dedicated connections that don't pool cleanly through PgBouncer in transaction mode. Fine at self-host scale; reintroduce Redis only when a metric forces it.
One caveat to accept consciously: Postgres-first makes the database a single failure domain for data + queue + cache + search. On a single-node self-hosted deployment that coupling already exists (it is one box), so the simplicity is worth it. The §23.4 swaps exist to decouple these when you outgrow one box — which, per the source's own framing, most deployments never do ([S24]).
Concrete choices for the NestJS API (apps/api), pinned now. The remaining concerns (FTS, vector, advisory locks, LISTEN/NOTIFY pub-sub) are settled during per-feature implementation planning.
| Concern | Choice | Notes |
|---|---|---|
| Queue + scheduled jobs | pg-boss via @wavezync/nestjs-pgboss ([S25]) |
pg-boss is the SKIP LOCKED + LISTEN/NOTIFY pattern productized — with retries/backoff, dead-letter, priorities, concurrency, and archiving (SPEC §9.6). We do not hand-roll a job_queue table. |
| Cache (ephemeral TTL values) | raw UNLOGGED cache table via Drizzle, behind a NestJS CacheService interface |
UNLOGGED skips WAL for speed; upsert + expiry-on-read + periodic cleanup. Swappable to Redis behind the interface (§23.4). Reach for @nestjs/cache-manager only when decorator / HTTP-response caching is actually needed. |
Constraints that drove these choices:
pg_cronruns SQL, not application code. A scheduled run must enqueue app work (create a run, wake the worker), so scheduling uses pg-boss cron — which needs no extension and works on any managed Postgres.pg_cronstays optional, only for pure-SQL DB maintenance on deployments whose Postgres ships the extension.- pg-boss uses the
pgdriver, separate from Drizzle'spostgres.js. The API therefore runs two Postgres drivers / pools against the same database — expected (no mature postgres.js-native queue exists), not a problem. - Authoritative state is never "cache." Sessions, config, and run state are real Drizzle tables; the
CacheServiceholds only ephemeral, regenerable values (rate-limit counters, provider-health, dedup).
Use PostgreSQL for:
- Users.
- Groups.
- Memberships.
- Policies.
- Config.
- Projects.
- Chats.
- Messages.
- Runs.
- Run events.
- Todos.
- Artifacts metadata.
- Knowledge metadata.
- Credential metadata.
- Audit events.
Use Row Level Security where practical, but do not rely on it as the only authorization layer.
Object storage is the one need that does not fold into Postgres (§24.0). MVP default is a local filesystem volume behind an S3-compatible interface (e.g. an S3 client pointed at a local gateway, or a thin filesystem driver), so code is written against the S3 API and the backing store swaps to MinIO/S3 at scale without changes.
Stored here:
- Uploaded files.
- Artifact content that is binary or large (text artifacts — Markdown, HTML, code — live in
artifact_versions, §24.0). - Export bundles.
- Parsed document representations.
- Large run logs.
- Sandbox outputs.
- Backups.
Scale swap: MinIO (self-hosted) or S3/R2 (managed) when you need large binaries, multi-node access, or CDN delivery (§23.4).
MVP with pgvector:
- Simple self-hosting.
- Consistent backup with Postgres.
- Good enough for moderate scale.
Scale swap to Qdrant (the single chosen alternative, §23.4) if:
- Embeddings exceed comfortable Postgres size.
- Low-latency high-volume vector retrieval is needed.
- Multi-tenant vector isolation becomes complex.
- Advanced hybrid/reranking features are needed.
MVP: PostgreSQL FTS (no separate engine).
Scale swap (§23.4): Meilisearch for richer search UX, OpenSearch at enterprise corpus scale. One of these, only when FTS is the measured bottleneck.
No Redis in MVP. The needs Redis usually covers are met inside Postgres (§24.0):
- Queue + scheduled jobs: pg-boss (
SKIP LOCKED+LISTEN/NOTIFY). - Short-lived locks: advisory locks (
pg_advisory_lock). - Sessions: DB-backed via the NextAuth Drizzle adapter (already in use).
- Rate limits / model-provider health cache / webhook dedup / temporary stream state: small tables,
UNLOGGEDwhere durability is not required, withexpires_at+ periodic cleanup.
Authoritative run state always lives in durable Postgres tables (run-event store, §9.4), never only in an ephemeral cache. Introduce Redis only as the §23.4 scale swap for a high-QPS hot cache.
The SQL below is illustrative. The source of truth is the Drizzle schema in apps/api/src/db/schema/* (auth.ts, chats.ts, …), with migrations generated by drizzle-kit. Treat these tables as the target shape to grow that schema toward, not hand-written DDL to run.
users(id, email, display_name, avatar_url, status, created_at, updated_at)
external_identities(id, user_id, provider, external_subject, metadata_json, created_at)
org_units(id, parent_id, type, name, path, settings_json, created_at, updated_at)
memberships(id, user_id, org_unit_id, role, inherited_from_id, created_at)
roles(id, scope_type, scope_id, name, permissions_json)
policies(id, scope_type, scope_id, effect, action, resource_type, resource_id, conditions_json, version, created_at)configs(id, scope_type, scope_id, config_json, version, created_by, created_at)
provider_accounts(id, owner_scope_type, owner_scope_id, provider_type, display_name, base_url, enabled, metadata_json)
credentials(id, owner_scope_type, owner_scope_id, provider_account_id, secret_type, encrypted_payload, key_version, expires_at)
run_config_snapshots(id, run_id, snapshot_json, created_at)projects(id, owner_scope_type, owner_scope_id, name, description, visibility, settings_json, created_at, updated_at)
project_members(id, project_id, user_id, role, created_at)
chats(id, project_id, owner_user_id, title, summary, settings_json, visibility, created_at, updated_at)
messages(id, chat_id, sender_type, sender_id, content_json, source_channel_id, created_at, edited_at)
runs(id, chat_id, message_id, user_id, status, worker_id, started_at, completed_at, error_json)
run_events(run_id, sequence, event_type, payload_json, created_at)goals(id, scope_type, scope_id, title, description, status, source_message_id, active_run_id, created_by, created_at, updated_at)
todo_items(id, scope_type, scope_id, title, description, status, priority, owner_type, assignee_user_id, parent_todo_id, created_by, updated_by, created_at, updated_at)
todo_dependencies(todo_id, depends_on_todo_id)
todo_events(id, todo_id, event_type, actor_type, actor_id, message_id, run_id, payload_json, created_at)commands(id, name, description, scope_type, scope_id, manifest_json, handler_type, handler_ref, required_permissions_json, enabled)
command_invocations(id, command_id, user_id, chat_id, project_id, message_id, run_id, args_json, result_json, created_at)
skills(id, name, version, package_uri, manifest_json, signature, trust_level, created_at)
skill_installations(id, skill_id, scope_type, scope_id, enabled, config_json, installed_by, installed_at)
skill_invocations(id, skill_id, run_id, user_id, status, capability_snapshot_json, created_at)connectors(id, provider_type, owner_scope_type, owner_scope_id, auth_mode, capabilities_json, settings_json, enabled)
mcp_servers(id, owner_scope_type, owner_scope_id, name, transport, endpoint, command_json, capabilities_json, policy_json, enabled)
tool_invocations(id, run_id, tool_name, connector_id, mcp_server_id, input_json, output_json, risk_level, approval_id, status, created_at)
approvals(id, run_id, user_id, action_type, resource_type, resource_id, prompt, status, decided_by, decided_at)knowledge_spaces(id, owner_scope_type, owner_scope_id, source_type, display_name, connector_id, root_ref, sync_mode, write_policy, indexing_policy_json)
documents(id, knowledge_space_id, external_id, canonical_uri, title, document_type, mime_type, content_hash, metadata_json, permission_fingerprint, last_source_updated_at, last_indexed_at)
document_chunks(id, document_id, chunk_index, heading_path, content_text, content_hash, metadata_json, embedding_id)
knowledge_edges(id, from_document_id, to_document_id, relation_type, metadata_json)
embeddings(id, owner_scope_type, owner_scope_id, model, vector, metadata_json, created_at)artifacts(id, project_id, chat_id, owner_user_id, title, artifact_type, current_version_id, visibility, execution_policy_json, source_run_id, source_message_id, github_repo_id, created_at, updated_at)
artifact_versions(id, artifact_id, version_number, content_ref, content_hash, metadata_json, created_by, source_run_id, created_at)
artifact_permissions(id, artifact_id, subject_type, subject_id, permission, created_at)
artifact_executions(id, artifact_id, version_id, run_id, sandbox_id, status, logs_ref, output_ref, created_at)audit_events(id, actor_type, actor_id, action, resource_type, resource_id, scope_type, scope_id, payload_json, created_at)Use REST for core CRUD and SSE/WebSockets for streams. Optionally add GraphQL later for complex admin screens.
POST /api/messages
GET /api/chats
POST /api/chats
GET /api/chats/{chat_id}
GET /api/chats/{chat_id}/messages
POST /api/chats/{chat_id}/messages
GET /api/runs/{run_id}
GET /api/runs/{run_id}/events
POST /api/runs/{run_id}/cancel
POST /api/runs/{run_id}/retryGET /api/projects
POST /api/projects
GET /api/projects/{project_id}
PATCH /api/projects/{project_id}
POST /api/projects/{project_id}/members
DELETE /api/projects/{project_id}/members/{user_id}
GET /api/projects/{project_id}/artifacts
GET /api/projects/{project_id}/todos
GET /api/projects/{project_id}/searchGET /api/commands?scope=chat:{id}
POST /api/commands/execute
GET /api/command-invocations/{id}POST /api/search
POST /api/retrieval/preview
GET /api/search/sources/{source_id}Example search request:
{
"query": "worker progress restore after page refresh",
"scopes": ["project:assistant"],
"sources": ["chats", "artifacts", "wiki", "project_docs"],
"mode": "hybrid",
"include_citations": true
}GET /api/connectors
POST /api/connectors
POST /api/connectors/{id}/oauth/start
GET /api/connectors/{id}/oauth/callback
POST /api/connectors/{id}/sync
GET /api/connectors/{id}/resourcesPOST /api/artifacts
GET /api/artifacts/{artifact_id}
GET /api/artifacts/{artifact_id}/versions
POST /api/artifacts/{artifact_id}/versions
POST /api/artifacts/{artifact_id}/execute
POST /api/artifacts/{artifact_id}/share
POST /api/artifacts/{artifact_id}/exportGET /api/admin/org-units
POST /api/admin/org-units
PATCH /api/admin/org-units/{id}
GET /api/admin/policies
POST /api/admin/policies
GET /api/admin/audit-events
GET /api/admin/run-traces
GET /api/admin/costsDefault services:
web
api
worker
postgres # pgvector image; also runs the queue, scheduler, cache, FTS, vectors (§24.0)
sandbox-runner
caddy
Optional profiles:
cache-redis # scale swap: high-QPS cache / queue (§23.4, §24.0)
objectstore-minio # scale swap: large binary object storage (§23.4)
vector-qdrant
search-meilisearch
channels
observability
local-llm-ollama
mcp-gateway
browser-computer-use
Core compose should include:
- Named volumes.
- Health checks.
- Dependency health gates.
- Non-root containers.
- Read-only filesystems where possible.
- Secrets through env files or mounted secret files.
- Backup container/profile.
- Migration job.
- Versioned image tags.
.env.example.docker compose pull && docker compose up -dupgrade path.
Required:
- Semantic versioning.
- Database migrations with rollback guidance.
- Config schema versioning.
- Pre-upgrade backup command.
- Migration dry-run option.
- Health check after migration.
- Release notes with breaking changes.
- Automatic detection of incompatible plugins/skills.
- Option to disable untrusted skills after upgrade.
Backup:
- PostgreSQL dump or physical backup.
- Object storage snapshot.
- Config export.
- Encryption key backup warning.
- Optional vector/search rebuild from source data.
Restore:
- Restore DB.
- Restore object storage.
- Restore keys.
- Re-run indexers.
- Verify admin login.
- Verify run/artifact references.
Local-only mode disables:
- External model providers unless user configures.
- External telemetry.
- Public artifact sharing.
- Remote MCP servers by default.
- External network access from sandboxes.
- Marketplace downloads.
- No public unauthenticated access.
- First admin setup token displayed once in logs or generated file.
- Strong session cookies.
- CSRF protection.
- CORS locked down.
- Rate limits.
- Audit logging enabled.
- All secret payloads encrypted.
- Tool approvals enabled by default.
- Shell/sandbox disabled until configured.
- Public sharing disabled by default.
- Treat retrieved content, webpages, emails, documents, and tool outputs as untrusted data.
- Separate system/user instructions from retrieved data.
- Warn when retrieved data asks the agent to ignore instructions or exfiltrate data.
- Require explicit approval before actions based on untrusted content.
- Track source-to-action provenance.
- Use tool-specific allowlists.
- Make the model explain tool actions before execution when risk is high.
- Signed packages for marketplace/distributed skills.
- Admin approval for executable skills.
- Static and dynamic scanning.
- Capability manifests.
- Sandboxed execution.
- Network egress restrictions.
- Secret access restrictions.
- Skill version pinning.
- Fast revocation/disable.
- Trust badges in UI.
Adopt MCP security principles:
- User consent and control.
- Explicit data sharing permissions.
- Tool behavior descriptions are untrusted unless server is trusted.
- Tool calls require appropriate user consent.
- LLM sampling requests from MCP servers must be approved and bounded.
- Remote MCP servers cannot receive arbitrary prompt/context unless permitted.
- All queries enforce project/group/user access.
- Search indexes include permission fingerprints.
- Artifact URLs require auth unless explicitly public.
- Shared projects do not leak private user memories.
- User credentials are not visible to project admins unless explicitly shared.
- Project credentials are not usable outside project scope.
- No host Docker socket.
- No privileged containers.
- CPU/memory/process limits.
- Ephemeral filesystems.
- Explicit mounts.
- Network deny by default.
- Egress allowlists.
- Secret injection only per approved run.
- Cleanup after execution.
- Logs retained by policy.
Every run should expose a debug view with:
- User message.
- Effective config snapshot.
- Selected model/provider.
- Retrieved sources.
- Prompt/context outline, redacted as needed.
- Tool calls.
- Approvals.
- Skill activations.
- Todo updates.
- Artifact versions.
- Token/cost metrics.
- Errors and retries.
Audit:
- Login/logout.
- Credential created/used/rotated/deleted.
- Connector added/removed.
- MCP server invoked.
- Skill installed/enabled/invoked.
- Tool approval requested/approved/denied.
- Artifact shared/published/deleted.
- Project shared.
- Policy changed.
- Admin action.
- Data export.
- Public link created.
Track:
- Run latency.
- Queue depth.
- Worker utilization.
- Model latency.
- Tool latency.
- Retrieval latency.
- Embedding throughput.
- Sandbox failures.
- Token/cost usage.
- Error rates.
- Approval rates.
- Search success signals.
- Artifact generation success.
- Inbox / Recent chats.
- Projects.
- Search.
- Wiki / Knowledge Spaces.
- Artifacts.
- Goals & Todos.
- Connectors.
- Skills.
- Admin.
Must include:
- Message stream.
- Active goal.
- Todo/progress panel.
- Artifact side panel.
- Source citations panel.
- Command autocomplete.
- Model selector, if allowed.
- Connector status.
- Approval prompts.
- Run status and event log.
- Resume/retry/cancel controls.
Must include:
- Project overview.
- Members.
- Chats.
- Artifacts.
- Todos.
- Knowledge sources.
- Connectors/MCP.
- Skills/commands.
- GitHub repositories.
- Runs.
- Settings.
- Audit.
Must include:
- Users.
- Nested groups.
- Memberships.
- Provider accounts.
- Credential policy.
- Connectors.
- MCP servers.
- Skills.
- Model policies.
- Budgets.
- Audit logs.
- System health.
- Backups/upgrades.
Search should support:
- Global search.
- Project search.
- Chat history search.
- Wiki search.
- Artifact search.
- Filters by source, date, author, project, tag, type.
- Result preview.
- Open source.
- Use selected results as context.
Tracking: the MVP is executed as GitHub milestones (v0.1 → v1.0) and issues — see ROADMAP.md for the milestone ↔ issue map. The must-haves below map onto those milestones; this section stays the canonical scope definition, the tracker holds live status.
- Multi-user accounts.
- Nested groups and memberships.
- Project creation and sharing.
- Global/group/project/user/chat config model.
- Config Resolver with run snapshots.
- Basic RBAC and deny policies.
- BYOK provider credentials at user and instance scope.
- OpenAI-compatible, Anthropic, Ollama/local provider support.
- Chat UI with async worker runs.
- Run event stream with refresh recovery.
- Basic slash command registry.
/goal,/todo,/search,/project,/model,/connect,/artifact.- Durable goals and todos.
- Chat summaries.
- Chat/project/artifact full-text search.
- pgvector embeddings for chats, summaries, docs, and artifacts.
- Project documents and artifact storage.
- Markdown and HTML artifacts with version history.
- Docker sandbox for explicitly approved artifact/code execution.
- MCP server registry with stdio and HTTP support.
- Connector framework with GitHub, local filesystem read-only, Notion read-only, and Telegram or Discord.
- Admin-installed skills using the single-
SKILL.mdAgent Skills format (noskill.yaml). - Skill capability declarations and audit logs.
- Obsidian/local Markdown Knowledge Space read-only indexing.
- Notion Knowledge Space read-only indexing through OAuth or token.
- Docker Compose deployment.
- Backup/restore script.
- Audit logs.
- Basic observability.
- Slack channel.
- MinIO object-store profile (filesystem volume is the default; MinIO for large binaries at scale).
- Meilisearch profile.
- Qdrant profile.
- Web push notifications.
- Artifact export to PDF/DOCX.
- GitHub PR creation.
- Simple scheduled runs.
- Advanced artifact editor.
- GitHub branch/worktree integration.
- Slack and email channels.
- More connector OAuth flows.
- RAG retrieval evaluation UI.
- Project templates.
- Better admin policy UI.
- Skill signing for internal packages.
- Background scheduled tasks.
- Webhook-triggered runs.
- Agent teams/subagents.
- Parallel work units.
- Workflow builder.
- Visual run graph.
- Fine-grained artifact permissions.
- Bidirectional wiki writes with approval.
- Mobile app/PWA offline cache.
- Local browser/computer-use sandbox.
- Enterprise SSO/OIDC/SAML.
- Per-project cost budgets.
- Stable plugin/skill SDK.
- Public API.
- MCP server mode exposing assistant/project resources.
- Advanced compliance export.
- Multi-node deployment.
- HA-ready Postgres/Object storage docs.
- Trust center/security hardening guide.
- Signed skill marketplace for trusted/private registries.
- Full disaster recovery guide.
Should the primary backend be Go or TypeScript?Resolved (v0.2): TypeScript / NestJS — already shipping inapps/api. The whole platform is one TypeScript stack (§23).Should pgvector be the only MVP vector store or should Qdrant be available from day one?Resolved (v0.2): pgvector only for MVP; Qdrant is the single scale swap (§23.4, §24.3).- Should artifact execution be enabled in MVP or hidden behind an advanced profile?
- Should personal wiki writes be supported in MVP or read-only only?
- Should the first messaging channel be Telegram, Discord, or Slack?
- Should the system provide its own MCP servers for projects/artifacts/search from MVP?
- Should user-owned BYOK credentials be allowed inside shared projects by default?
- How much run trace should be visible to normal users versus admins?
- What is the minimum skill signing model for private self-hosted deployments?
- Should there be family-specific controls distinct from enterprise controls?
These are improvements noticed during the v0.2 refinement but kept out of the spec body because they are design preferences, not contradiction fixes or stack-narrowing. Accept or reject explicitly.
- Split the run
statusenum (§9.3). It currently mixes lifecycle (queued,completed,failed,cancelled,expired) with running sub-phases (running_model,running_tool,running_sandbox,summarizing, …). Cleaner:status∈ {queued, running, waiting_approval, completed, failed, cancelled, expired} plus a separatephasecolumn for the running sub-state. You then know which phase a run failed in without overloading one field;run_eventsalready carries the phase detail. LangGraph's node model maps naturally tophase. - Drop the
embedding_idindirection (§25.7). With a single pgvector store,document_chunkscan hold theembedding vectorcolumn directly (keep amodelcolumn for multi-model support) instead of pointing at a separateembeddingstable. Removes a join and a write per chunk; reverse only if you actually need many embedding models per chunk simultaneously. - Name the project. The repo is
llame; the spec title is generic. Pick the product name once and use it throughout.
The MVP is acceptable when:
- A fresh user can deploy with Docker Compose, create an admin account, connect a model provider, and chat.
- A user message creates a durable run processed by a worker.
- Refreshing the page during a run restores progress from persisted events.
- An admin can create nested groups and assign users.
- A project can be shared with a group.
- A project can have its own settings, knowledge sources, MCP servers, skills, and artifacts.
- A user can add their own model API key without instance-level provider configuration.
- A chat can use project docs, chat history, and wiki content through hybrid search.
- Search can find relevant chats by title, message content, summary, and embeddings.
- The assistant can create and update todos during a long task.
/goaland/todowork as durable commands.- An admin can install a skill and see audit logs when it runs.
- The assistant can create a versioned Markdown/HTML artifact.
- A basic GitHub connector can read repository context.
- A Telegram or Discord bot can send a message into the same run system.
- Tool calls and sensitive actions require approval according to policy.
- Audit logs can answer who did what, when, and through which connector/tool.
Potential future features/follow-ups: realtime voice and meeting assistant support for calls and transcripts; browser/computer-use automation with stronger sandboxes; visual workflow builder for scheduled and event-driven automations; agent teams and debate/review modes for complex work; private skill marketplace with signing, reputation, and vulnerability feeds; cross-instance project federation for families or partner organizations; encrypted local-first mobile sync; advanced compliance packs for regulated teams; model-evaluation loops that learn from user corrections without leaking private data; and richer artifact hosting for full internal tools with backends, databases, and deployment pipelines.
[S1] Model Context Protocol introduction and specification: https://modelcontextprotocol.io/docs/getting-started/intro and https://modelcontextprotocol.io/specification/2025-06-18
[S2] Claude Code skills docs: https://code.claude.com/docs/en/slash-commands
[S3] Claude Code command reference: https://code.claude.com/docs/en/commands
[S4] Claude Code artifacts docs: https://code.claude.com/docs/en/artifacts
[S5] OpenAI Codex CLI docs: https://developers.openai.com/codex/cli
[S6] OpenAI Codex slash commands / app commands docs: https://developers.openai.com/codex/cli/slash-commands and https://developers.openai.com/codex/app/commands
[S7] Open WebUI docs: https://docs.openwebui.com/
[S8] LibreChat docs: https://www.librechat.ai/docs/
[S9] AnythingLLM docs: https://docs.anythingllm.com/
[S10] Dify Docker Compose self-hosting docs: https://docs.dify.ai/en/self-host/deploy/quick-start/docker-compose
[S11] Dify Knowledge/RAG docs: https://docs.dify.ai/en/cloud/use-dify/knowledge/readme
[S12] LobeHub docs: https://lobehub.com/docs/usage/start
[S13] OpenClaw project (primary sources): https://github.com/openclaw/openclaw and https://openclaw.ai/
[S14] OpenClaw skill-ecosystem security risks (TechRadar): https://www.techradar.com/pro/here-are-the-openclaw-security-risks-you-should-know-about
[S15] ClawHub supply-chain / ranking-manipulation analysis (Silverfort): https://www.silverfort.com/blog/clawhub-vulnerability-enables-attackers-to-manipulate-rankings-to-become-the-number-one-skill/
[S16] Claude Code MCP docs: https://code.claude.com/docs/en/mcp
[S17] MCP GitHub organization: https://github.com/modelcontextprotocol
[S18] Khoj docs: https://docs.khoj.dev/
[S19] Notion API overview: https://developers.notion.com/guides/get-started/overview
[S20] Notion authorization docs: https://developers.notion.com/guides/get-started/authorization
[S21] n8n advanced AI and platform docs: https://docs.n8n.io/advanced-ai/
[S22] Agent Skills open specification and SKILL.md format: https://agentskills.io/specification and https://github.com/anthropics/skills
[S23] Malicious OpenClaw skills / macOS infostealers (TechRadar): https://www.techradar.com/pro/security/multiple-malicious-openclaw-skills-found-online-including-two-macos-infostealers
[S24] "You Just Need Postgres" — Postgres-for-everything rationale and feature mapping (Lucas Andrade): https://youjustneedpostgres.com and https://github.com/olucasandrade
[S25] pg-boss — Postgres-backed job queue for Node (SKIP LOCKED, scheduling, retries): https://github.com/timgit/pg-boss
[S26] Hermes Agent (Nous Research) — persistence & memory architecture (SQLite/FTS5 episodic, session lineage, scan-on-write, unbounded-growth caveat): https://hermes-agent.nousresearch.com/docs/developer-guide/architecture and https://www.glukhov.org/ai-systems/hermes/hermes-agent-memory-system/
[S27] OpenClaw memory model — file-first source of truth with a derived SQLite (FTS5 + sqlite-vec) index: https://docs.openclaw.ai/concepts/memory and https://www.pingcap.com/blog/local-first-rag-using-sqlite-ai-agent-memory-openclaw/
- v0.3 (2026-06-28): Round-1 iterative review (3 parallel independent reviewers, primary-source verified). Fixes: dropped the false "valid Agent Skill on any compliant host" portability claim and the non-standard top-level
when_to_use/disable_model_invocationfrontmatter keys (§12.2–§12.3), with exact Agent-Skills field constraints explicitly deferred to the skill-system implementation stage; flagged that the AI SDK / LangGraph agent layer currently lives inapps/weband must migrate to the worker for the §9.5 boundary, and marked AI SDK v5 / NextAuth v5 as beta (§23.1); folded the §21.1 diagram's separate pgvector/FTS cylinders into a single PostgreSQL node (Postgres-first consistency); softened the pg-bossLISTEN/NOTIFYclaim and correctedpg_trgmfrom "built-in" to "contrib extension" (§9.6, §24.0); fixed the §22.9§32.3→§32.2cross-reference; corrected the §2.1 OpenClaw security citation ([S4]→[S23]). Reviewers independently confirmed the OpenClaw / ClawHub facts and the single-SKILL.mdformat as accurate. Exact-standard nitpicks (precisemetadatavalue types,allowed-toolsencoding, PgBouncer pooling behavior) were deliberately deferred to feature-planning, not applied. - v0.2 (2026-06-28): Eliminated
skill.yaml/SKILL.mdduplication; narrowed to one TypeScript stack matching thellamemonorepo; split author-declared skill capabilities from platform-assigned trust; collapsed provider / search / vector / sandbox option menus to one MVP default + one scale swap; adopted Postgres-first (§24.0). Summary in the refinement note under the title. - v0.1: Initial draft.