Multi-tenant workspace isolation

t-703·WorkTask·
·
·
Created1 month ago·Updated1 week ago·pipeline runs →

Description

Edit

Multi-tenant workspace isolation for ava. Currently all users share ~/ava as a single workspace with hardcoded paths. Need per-chat workspaces, layered skills, role-based prompts, and safe tool scoping.

Architecture

Workspace layout:

~/ava/
├── shared/
│   └── skills/          # shared skills (migrated from current ~/ava/skills)
├── names.json           # chatId -> friendly name mapping
├── ben/                 # Ben's DM workspace (symlink target for chatId dir)
│   ├── downloads/       # telegram file downloads
│   ├── skills/          # user-specific skills
│   ├── projects.md      # project brief
│   ├── heartbeat.md     # heartbeat checklist (owner only)
│   └── ...              # scratch files
├── 33193730 -> ben/     # chatId symlink to friendly name
├── sarah/               # another user's workspace
│   ├── downloads/
│   ├── skills/
│   └── ...
└── group-family/        # group chat workspace
    ├── downloads/
    └── ...

Roles

  • Owner (Ben, tgOwnerUserId): full access to all tools, all workspaces, omnirepo read, heartbeat
  • Authorized user (others in ALLOWED_USER_IDS): their own workspace, memory tools, web search, sandboxed bash/read_file (restricted to their workspace + shared paths)
  • Group chats get shared workspace keyed by group chatId

Phases

Phase 1: Per-chat workspace dirs + migration

  • Add Workspace.hs module with chatWorkspace, ensureWorkspace, resolveWorkspaceName
  • names.json maps chatId -> friendly name (user's first name, lowercased)
  • Workspace created on first message from a chat
  • Update scratchDir in system prompt (2 locations in Bot.hs)
  • Update telegram-downloads path
  • Update heartbeat.md path
  • Migrate existing ~/ava/ files to ~/ava/ben/ (Ben's DM = chatId 33193730)
  • Symlink ~/ava/33193730 -> ben/
  • Move ~/ava/skills to ~/ava/shared/skills, symlink back for Obsidian compat

Phase 2: Layered skills

  • Skill resolution: chat workspace skills -> shared skills -> repo skills
  • Thread chatId through to skill resolution
  • Update skillDirs in Tools.hs to accept workspace path

Phase 3: Per-user system prompt

  • Replace isBenAuthorized (string match on "ben") with isOwner (userId == tgOwnerUserId)
  • Owner prompt: full project context, repo access, research instructions
  • Authorized user prompt: scoped to their workspace, no repo references
  • Group prompt: engagement classifier + group-scoped
  • Per-chat projects.md instead of global

Phase 4: Safe tool scoping for non-owner users

  • run_bash: default CWD set to user's workspace, blocked dangerous commands (dd, rm -rf /, mkfs, etc.)
  • read_file: restricted to user workspace + ~/ava/shared/ + /tmp
  • write_file: restricted to user workspace
  • Owner keeps unrestricted access
  • Dangerous command blocklist: dd, mkfs, fdisk, mount, umount, rm -rf /, chmod -R, chown -R on system dirs

Key decisions

  • Workspace key: chatId (matches memory scoping)
  • Group chats: shared workspace per group
  • names.json: auto-populated from user's first name on first message
  • Authorized users get bash but with workspace sandboxing + command blocklist
  • Owner is unrestricted
  • Obsidian vault stays at ~/ava/shared/skills with symlink from old location

Timeline (6)

🔄[human]Open → Review1 month ago
💬[human]1 month ago

All 4 phases implemented and deployed:

  • Phase 1 (t-704): Per-chat workspace dirs + migration (02e26bf)
  • Phase 2 (t-705): Layered skills resolution (f49e573)
  • Phase 3 (t-706): Per-user system prompt + proper auth (26ac96a)
  • Phase 4 (t-707): Safe tool scoping for non-owner users (6362f1a)

Filesystem migration done: ~/ava/ben/ has Ben's files, ~/ava/shared/skills/ has Obsidian vault, ~/ava/skills symlinks to shared/skills for backwards compat.

💬[human]1 week ago

Ava verified: commit in live history, shipping complete. Moving to Verified.

🔄[human]Review → Verified1 week ago