diff --git a/docs/MVP.md b/docs/MVP.md deleted file mode 100644 index 920a439c74..0000000000 --- a/docs/MVP.md +++ /dev/null @@ -1,459 +0,0 @@ ---- - -```md -# Cross-Platform Tauri Agent Assistant — MVP Specification - -> **Current product docs:** we **ship and support desktop only** (Windows, macOS, Linux). This spec still describes a **broader platform vision** (including mobile) for engineering planning—it is not a promise of availability on Android/iOS or web yet. - -## Overview - -This MVP defines a **Telegram-based Agent Assistant** built with **Tauri (Rust + Web UI)** targeting: - -- Windows -- macOS -- Android -- iOS - -The assistant: -- Interacts with users via a **Telegram bot (DM-first)** -- Processes Telegram channel data **locally on device** -- Uses a **minimal backend** only for: - - identity & login - - payments & entitlements - - push notifications (especially for iOS) -- Avoids storing Telegram message content on servers - ---- - -## Core Platform Behavior Summary - -| Platform | Listening Model | Trigger to Respond | -| -------- | ------------------------------- | -------------------------- | -| Windows | Continuous (background) | Bot DM or channel activity | -| macOS | Continuous (background) | Bot DM or channel activity | -| Android | Continuous (foreground service) | Bot DM or channel activity | -| iOS | On-demand only | Bot DM → push → tap → sync | - ---- - -## Architectural Pillars - -- **UI-first development** -- **Single Rust agent runtime** -- **Telegram bot as the user interface** -- **Privacy-first local processing** -- **Backend as infrastructure, not intelligence** - ---- - -# PHASED MVP PLAN - ---- - -## Phase 0 — Project Skeleton & Tooling - -### Goals - -- Prepare repo structure -- Establish documentation and contribution rules -- No business logic yet - -### Deliverables - -- Monorepo structure -- Tauri project scaffold -- Mobile targets enabled (Tauri v2) -- CI hooks (optional) - -### Repo Structure - -``` - -/apps -/desktop -/mobile -/core -/agent-runtime (Rust) -/storage -/telegram -/backend -/docs - -``` - -### Documentation Commands (Required) - -```bash -/docs/architecture.md # high-level system design -/docs/decisions/ADR-000.md # initial architecture decision record -``` - -### Exit Criteria - -- App builds and runs (blank UI) -- Docs folder initialized -- ADR process agreed upon - ---- - -## Phase 1 — UI-First MVP (No Logic) - -### Goals - -Build the **entire UI flow** before implementing logic. - -### UI Screens - -- Login / Signup -- Telegram Connect (bot instructions) -- Channel Selection -- Sync Status Screen -- Settings (background, privacy, storage) -- Plan & Billing (stub) -- Logs / Activity View (local only) - -### Platforms - -- All platforms (desktop + mobile) - -### Deliverables - -- Responsive UI -- Navigation between screens -- Mock data only -- No Telegram, no backend, no Rust logic - -### Documentation Commands - -```bash -/docs/ui/flows.md # user flows -/docs/ui/screens.md # screen definitions -/docs/ui/states.md # loading / error / empty states -``` - -### Exit Criteria - -- Entire app is navigable -- No dead-end screens -- UI approved before logic begins - ---- - -## Phase 2 — Local Agent Runtime (Rust Only) - -### Goals - -Implement the **local agent engine** without Telegram or backend. - -### Components - -- Rust agent runtime -- Intent router (question / sync / config) -- Processing pipeline (stubbed) -- Response composer (mock responses) - -### Deliverables - -- Tauri IPC commands: - - `agent_init` - - `agent_process_query` - - `agent_status` - -- In-memory only state - -### Documentation Commands - -```bash -/docs/core/agent.md # agent architecture -/docs/core/events.md # internal event types -/docs/core/state.md # memory state model -``` - -### Exit Criteria - -- UI can send a question -- Agent returns a mock response -- No persistence yet - ---- - -## Phase 3 — Local Storage & Privacy Layer - -### Goals - -Add **efficient, privacy-first local storage**. - -### Storage Rules - -- No Telegram message bodies by default -- Store only: - - channel IDs - - last processed message IDs - - dedupe hashes - -- Encrypted at rest - -### Deliverables - -- Encrypted SQLite -- OS keychain integration -- Storage abstraction in Rust -- “Ephemeral mode” toggle - -### Documentation Commands - -```bash -/docs/storage/schema.md -/docs/storage/encryption.md -/docs/privacy/model.md -``` - -### Exit Criteria - -- App restarts without losing cursors -- “Delete local data” wipes all state -- No plaintext sensitive data on disk - ---- - -## Phase 4 — Telegram Bot Integration (Agent Assistant) - -### Goals - -Turn the app into a **real Telegram agent assistant**. - -### Telegram Capabilities (MVP) - -- Bot DM interaction -- Read user questions -- Fetch channel messages (where bot has access) -- Reply via DM - -### Platform Behavior - -- Windows/macOS/Android: continuous polling -- iOS: no polling (on-demand only) - -### Deliverables - -- Telegram Bot Gateway (Rust) -- Update polling / fetching -- Message dedupe + cursoring -- Agent replies sent via bot DM - -### Documentation Commands - -```bash -/docs/telegram/bot.md -/docs/telegram/flows.md -/docs/telegram/limits.md -``` - -### Exit Criteria - -- User asks a question in Telegram -- App processes it -- Bot replies correctly -- No duplicate replies - ---- - -## Phase 5 — Platform Background Execution - -### Goals - -Enable **platform-appropriate background behavior**. - -### Platform Breakdown - -#### Windows - -- Tray app -- Autostart -- Background polling - -#### macOS - -- Menu bar app -- Launch at login - -#### Android - -- Foreground service (persistent notification) -- Background polling allowed - -#### iOS - -- ❌ No continuous background -- Only foreground execution - -### Documentation Commands - -```bash -/docs/platforms/windows.md -/docs/platforms/macos.md -/docs/platforms/android.md -/docs/platforms/ios.md -``` - -### Exit Criteria - -- Desktop apps run without UI open -- Android foreground service stable -- iOS behaves strictly foreground-only - ---- - -## Phase 6 — Minimal Backend Integration - -### Goals - -Introduce backend **without violating privacy goals**. - -### Backend Responsibilities - -- Authentication -- Device registration -- Entitlements -- Push notifications -- Payment verification - -### Explicit Non-Responsibilities - -- No Telegram message storage -- No agent logic -- No summaries - -### Deliverables - -- Auth flow wired into UI -- Entitlements fetched on startup -- Device registered for push - -### Documentation Commands - -```bash -/docs/backend/api.md -/docs/backend/data-model.md -/docs/backend/security.md -``` - -### Exit Criteria - -- User login works -- Entitlements enforced locally -- Backend DB contains no message content - ---- - -## Phase 7 — iOS Push → Tap → Sync Flow - -### Goals - -Implement the **iOS-specific agent interaction model**. - -### Flow - -1. User sends question to bot -2. Backend triggers visible push -3. User taps notification -4. App opens -5. App syncs Telegram -6. Agent processes -7. Bot replies - -### Deliverables - -- APNs integration -- Push payload handling -- Sync-on-open logic - -### Documentation Commands - -```bash -/docs/ios/push-flow.md -/docs/ios/limitations.md -``` - -### Exit Criteria - -- Push reliably opens app -- Sync runs automatically -- Bot replies successfully - ---- - -## Phase 8 — Payments & Plan Gating - -### Goals - -Monetize safely and correctly. - -### Platforms - -- Desktop: Stripe / Paddle -- Android: Play Billing -- iOS: StoreKit - -### Deliverables - -- Purchase flow per platform -- Receipt verification -- Feature gating in Rust - -### Documentation Commands - -```bash -/docs/billing/plans.md -/docs/billing/verification.md -/docs/billing/entitlements.md -``` - -### Exit Criteria - -- Paid features unlock correctly -- Downgrades enforced -- Offline grace period handled - ---- - -## Phase 9 — Hardening & Release Prep - -### Goals - -Stability, observability, and trust. - -### Deliverables - -- Error handling -- Rate limiting -- Abuse prevention -- Crash-safe storage -- UX polish - -### Documentation Commands - -```bash -/docs/release/checklist.md -/docs/known-issues.md -/docs/security/threat-model.md -``` - -### Exit Criteria - -- No critical crashes -- No duplicate Telegram replies -- Clear user-facing error states - ---- - -## Final Notes - -- The **Telegram bot is the product interface** -- The **Tauri app is the execution engine** -- The **backend is infrastructure, not intelligence** -- iOS behavior is intentionally constrained for correctness - ---- - -END OF MVP diff --git a/docs/feat/daemon-lifecycle-management.md b/docs/feat/daemon-lifecycle-management.md deleted file mode 100644 index 4e102c4e4c..0000000000 --- a/docs/feat/daemon-lifecycle-management.md +++ /dev/null @@ -1,350 +0,0 @@ -# Daemon Lifecycle Management System - -**Status**: ✅ Implemented -**Version**: 1.0.0 -**Date**: February 2026 - -## Overview - -The Daemon Lifecycle Management System provides comprehensive frontend integration with the alphahuman Rust daemon, enabling real-time health monitoring, automatic lifecycle management, and user-friendly daemon controls throughout the application. - -## Background - -### Problem Statement - -The alphahuman application runs a sophisticated Rust daemon that manages critical backend services (gateway, channels, heartbeat, scheduler) and emits detailed health information every 5 seconds. However, the frontend previously had: - -- **Poor Visibility**: Daemon controls buried in developer console only -- **No Real-Time Monitoring**: Manual status checks with raw JSON output -- **No Automatic Management**: No startup detection or error recovery -- **Disconnected UX**: Health events from Rust were ignored by frontend -- **Manual Recovery**: Users had to manually restart failed services - -### Solution Overview - -This implementation creates a complete bridge between the Rust daemon's health system and the React frontend, providing: - -- Real-time health monitoring with visual indicators -- Automatic daemon startup and error recovery -- User-friendly health displays in main UI -- Enhanced developer tools with live status -- Coordinated daemon and socket connection management - -## Architecture - -### System Components - -``` -┌─────────────────────────────────────────────────────────────┐ -│ React Frontend │ -├─────────────────────────────────────────────────────────────┤ -│ UI Layer │ -│ ├── DaemonHealthIndicator (Main UI) │ -│ ├── DaemonHealthPanel (Detailed View) │ -│ └── Enhanced TauriCommandsPanel (Dev Tools) │ -├─────────────────────────────────────────────────────────────┤ -│ State Management │ -│ ├── daemonSlice.ts (Redux) │ -│ ├── useDaemonHealth.ts (React Hook) │ -│ └── useDaemonLifecycle.ts (Lifecycle Hook) │ -├─────────────────────────────────────────────────────────────┤ -│ Services │ -│ └── daemonHealthService.ts (Event Processing) │ -├─────────────────────────────────────────────────────────────┤ -│ Integration Layer │ -│ ├── tauriSocket.ts (Event Listening) │ -│ └── SocketProvider.tsx (Coordinated State) │ -└─────────────────────────────────────────────────────────────┘ - │ - Tauri Event Bridge - │ -┌─────────────────────────────────────────────────────────────┐ -│ Rust Daemon │ -├─────────────────────────────────────────────────────────────┤ -│ Health System (src-tauri/src/alphahuman/health/mod.rs) │ -│ ├── Component Health Tracking │ -│ ├── Health Snapshot Generation │ -│ └── Event Emission (every 5s) │ -├─────────────────────────────────────────────────────────────┤ -│ Daemon Supervisor (src-tauri/src/alphahuman/daemon/mod.rs) │ -│ ├── Component Management │ -│ ├── Automatic Restarts │ -│ └── Exponential Backoff │ -├─────────────────────────────────────────────────────────────┤ -│ Components │ -│ ├── Gateway (API Server) │ -│ ├── Channels (Communication) │ -│ ├── Heartbeat (Health Monitor) │ -│ └── Scheduler (Cron Jobs) │ -└─────────────────────────────────────────────────────────────┘ -``` - -### Event Flow - -1. **Health Generation**: Rust daemon generates health snapshots every 5 seconds -2. **Event Emission**: Health data emitted via `alphahuman:health` Tauri event -3. **Frontend Processing**: `daemonHealthService` receives and parses events -4. **State Updates**: Redux state updated with component health information -5. **UI Reactivity**: React components automatically re-render with new status -6. **User Actions**: Manual controls trigger Tauri commands back to Rust - -## Implementation Details - -### Redux State Management - -**File**: `src/store/daemonSlice.ts` - -```typescript -interface DaemonUserState { - status: 'starting' | 'running' | 'error' | 'disconnected'; - healthSnapshot: HealthSnapshot | null; - components: { - gateway?: ComponentHealth; - channels?: ComponentHealth; - heartbeat?: ComponentHealth; - scheduler?: ComponentHealth; - }; - lastHealthUpdate: string | null; - connectionAttempts: number; - autoStartEnabled: boolean; - isRecovering: boolean; - healthTimeoutId: string | null; -} -``` - -**Key Features**: - -- Per-user daemon state isolation (following existing patterns) -- Component-level health tracking with error details -- Connection attempt management with exponential backoff -- Auto-start preference persistence -- Timeout handling for health event detection - -### Health Monitoring Service - -**File**: `src/services/daemonHealthService.ts` - -```typescript -export class DaemonHealthService { - private healthTimeoutId: NodeJS.Timeout | null = null; - private readonly HEALTH_TIMEOUT_MS = 30000; // 30 seconds - - async setupHealthListener(): Promise; - private parseHealthSnapshot(payload: unknown): HealthSnapshot | null; - private updateReduxFromHealth(snapshot: HealthSnapshot): void; - startHealthTimeout(): void; -} -``` - -**Responsibilities**: - -- Listen for `alphahuman:health` Tauri events from Rust -- Parse and validate health snapshot data -- Update Redux state with component health information -- Manage 30-second timeout detection for disconnected daemon -- Handle cleanup and error recovery - -### UI Components - -#### DaemonHealthIndicator - -**File**: `src/components/daemon/DaemonHealthIndicator.tsx` - -**Purpose**: Compact status indicator for main application UI - -**Visual States**: - -- 🟢 **Green**: All components running healthy (`status: 'running'`) -- 🟡 **Yellow**: Daemon starting or recovering (`status: 'starting'`) -- 🔴 **Red**: One or more components in error state (`status: 'error'`) -- ⚪ **Gray**: Daemon disconnected or not running (`status: 'disconnected'`) - -**Features**: - -- Click to open detailed health panel -- Tooltip showing component health summary -- Responsive sizing (sm/md/lg variants) -- Only visible in Tauri environments - -#### DaemonHealthPanel - -**File**: `src/components/daemon/DaemonHealthPanel.tsx` - -**Purpose**: Detailed health breakdown and manual controls - -**Features**: - -- Component health table with status, last update, restart counts -- Manual restart buttons for individual components -- Auto-start toggle with persistence -- Connection retry controls -- Real-time health information display -- User-friendly error messages and troubleshooting hints - -### Lifecycle Management - -**File**: `src/hooks/useDaemonLifecycle.ts` - -**Automatic Behaviors**: - -1. **App Startup**: - - Detect existing daemon status - - Auto-start daemon if enabled and not running - - Setup health monitoring listeners - -2. **Error Recovery**: - - Exponential backoff retry attempts (1s → 2s → 4s → 8s → 30s max) - - Maximum 5 retry attempts before requiring manual intervention - - Clear error states on successful recovery - -3. **Background Handling**: - - Maintain health monitoring when app backgrounded - - Reconnection logic without page refresh - - Coordinate with socket connection management - -4. **Cleanup**: - - Proper event listener cleanup on unmount - - Timeout cancellation to prevent memory leaks - - Graceful shutdown coordination - -### Integration Points - -#### Enhanced Service Management - -**File**: `src/components/settings/panels/TauriCommandsPanel.tsx` - -**Improvements to Existing Developer Console**: - -- **Live Status Display**: Real-time daemon status with PID and uptime -- **Component Health Grid**: Visual status for all daemon components -- **Enhanced Controls**: Smart start/stop buttons with proper loading states -- **Auto-Start Toggle**: User preference for automatic daemon startup -- **Connection Tracking**: Display retry attempts and recovery status -- **Better Error Messages**: User-friendly errors with troubleshooting hints - -**Before vs After**: - -```typescript -// Before: Manual status check - run(alphahumanServiceStatus, 'serviceStatus')}> - Status - - -// After: Live status display -
- -
-
Daemon Status: {status}
-
PID: {pid} | Uptime: {uptime}
-
-
-``` - -#### Socket Provider Integration - -**File**: `src/providers/SocketProvider.tsx` - -**Coordinated State Management**: - -- Check daemon health before attempting socket connections -- Display daemon-related errors in socket connection status -- Coordinate daemon startup with socket connection flows -- Provide daemon health context to socket consumers - -#### Main UI Integration - -**File**: `src/components/MiniSidebar.tsx` - -**User-Facing Integration**: - -- Daemon health indicator in main navigation (Tauri-only) -- Click to open detailed health modal -- Non-intrusive but easily accessible -- Consistent with existing UI patterns - -## User Experience - -### For End Users - -1. **Visible Status**: Daemon health indicator in main UI shows system status at a glance -2. **Automatic Operation**: Daemon starts automatically and recovers from errors without user intervention -3. **Clear Feedback**: User-friendly messages explain daemon state and provide actionable guidance -4. **Quick Access**: Click health indicator to see detailed component status and manual controls - -### For Developers - -1. **Enhanced Console**: Improved service management in settings with live status updates -2. **Component Monitoring**: Real-time visibility into all daemon components (gateway, channels, etc.) -3. **Debug Information**: Detailed health snapshots, retry attempts, and error history -4. **Manual Override**: Full control over daemon lifecycle with proper state management - -### Error Handling - -1. **Graceful Degradation**: System works properly in non-Tauri environments -2. **Timeout Management**: 30-second timeout detection with automatic recovery attempts -3. **User Guidance**: Clear error messages with troubleshooting suggestions -4. **Recovery Actions**: Manual restart options when automatic recovery fails - -## Technical Benefits - -### Performance - -- **Efficient Updates**: Debounced Redux updates prevent excessive re-renders -- **Memory Management**: Proper cleanup of timeouts and event listeners -- **Background Optimization**: Minimal resource usage when app backgrounded - -### Reliability - -- **Timeout Handling**: Robust detection of daemon disconnection -- **Exponential Backoff**: Smart retry logic prevents resource exhaustion -- **State Consistency**: Coordinated daemon and socket connection states -- **Error Recovery**: Automatic recovery from transient failures - -### Maintainability - -- **TypeScript**: Full type safety throughout the implementation -- **Modular Design**: Clear separation between state, services, and UI components -- **Existing Patterns**: Follows established Redux and component patterns -- **Testing**: Comprehensive error handling and edge case management - -## Configuration - -### Auto-Start Behavior - -Users can control daemon auto-start behavior through: - -1. **UI Toggle**: Available in both health panel and settings console -2. **Persistence**: Preference stored in Redux with persistence -3. **Default**: Auto-start enabled by default for better UX - -### Health Monitoring - -- **Event Frequency**: Rust daemon emits health every 5 seconds -- **Timeout Duration**: 30 seconds without health events = disconnected -- **Retry Logic**: Maximum 5 attempts with exponential backoff -- **Component Tracking**: Gateway, channels, heartbeat, scheduler components - -## Future Enhancements - -### Potential Improvements - -1. **Health History**: Track daemon health over time with charts/graphs -2. **Performance Metrics**: CPU/memory usage from daemon components -3. **Log Integration**: Show daemon logs directly in health panel -4. **Mobile Optimization**: Enhanced mobile-specific daemon management -5. **Notification System**: Push notifications for critical daemon events - -### Extensibility - -The architecture supports easy extension for: - -- Additional daemon components -- Custom health check logic -- Third-party integrations -- Advanced monitoring features - -## Conclusion - -The Daemon Lifecycle Management System provides a complete bridge between the sophisticated Rust daemon infrastructure and the React frontend, delivering excellent user experience while maintaining the technical robustness required for a production application. - -The implementation follows established patterns, provides comprehensive error handling, and creates a foundation for future daemon-related features while ensuring the system remains reliable and user-friendly. diff --git a/docs/gmail-skill-integration.md b/docs/gmail-skill-integration.md deleted file mode 100644 index 36614742f2..0000000000 --- a/docs/gmail-skill-integration.md +++ /dev/null @@ -1,627 +0,0 @@ -# Gmail Skill Integration — Design Reference - -**Scope:** Native Gmail skill — OAuth2 connect flow, tunnel-based callback, token storage, -initial inbox sync, and `send_email` tool dispatch. - -**Status:** Design reference / implementation guide. - ---- - -## 1. Overview - -This document describes the full lifecycle of connecting a Gmail skill to ZeroClaw. -It covers four concerns in order: - -1. **OAuth2 connect** — how the user authorises Gmail access -2. **Tunnel callback** — how the authorization code reaches the backend through the tunnel -3. **Token storage** — how credentials are encrypted and persisted in the auth profile store -4. **Email sync + send** — how the initial inbox is fetched and how sending works - -The skill is declared with the following manifest: - -```json -{ - "id": "gmail", - "name": "Gmail", - "version": "1.0.0", - "description": "Gmail integration via Google API — comprehensive email management with OAuth2 authentication, send/receive, labels, search, and attachments.", - "auto_start": false, - "platforms": ["windows", "macos", "linux", "android", "ios"], - "setup": { - "required": true, - "label": "Connect Gmail", - "oauth": { - "provider": "gmail", - "scopes": [ - "https://www.googleapis.com/auth/gmail.send", - "https://www.googleapis.com/auth/gmail.modify", - "https://www.googleapis.com/auth/gmail.labels" - ] - } - } -} -``` - -The equivalent `SKILL.toml` that ZeroClaw loads from -`~/.zeroclaw/workspace/skills/gmail/SKILL.toml` is shown in §2. - ---- - -## 2. Skill Manifest (`SKILL.toml`) - -Skills in ZeroClaw are loaded by `src/skills/mod.rs`. The loader reads a `SKILL.toml` -(or `SKILL.md`) file from `~/.zeroclaw/workspace/skills//`. The Rust struct it -populates is `Skill` / `SkillManifest` / `SkillTool`. - -`SKILL.toml` for Gmail: - -```toml -[skill] -name = "gmail" -version = "1.0.0" -description = "Gmail integration via Google API — send/receive, labels, search, attachments." -author = "zeroclaw" -tags = ["email", "google", "productivity"] - -# OAuth setup block — interpreted by the skill connect subsystem (see §3). -[skill.setup] -required = true -label = "Connect Gmail" - -[skill.setup.oauth] -provider = "gmail" -scopes = [ - "https://www.googleapis.com/auth/gmail.send", - "https://www.googleapis.com/auth/gmail.modify", - "https://www.googleapis.com/auth/gmail.labels", -] - -# Tools exposed to the agent after connection. - -[[tools]] -name = "gmail_send_email" -description = "Send an email via Gmail. Requires a connected Gmail account." -kind = "http" -command = "https://gmail.googleapis.com/gmail/v1/users/me/messages/send" - -[[tools]] -name = "gmail_list_messages" -description = "List messages in the Gmail inbox. Supports query filters." -kind = "http" -command = "https://gmail.googleapis.com/gmail/v1/users/me/messages" - -[[tools]] -name = "gmail_get_message" -description = "Fetch the full content of a single Gmail message by ID." -kind = "http" -command = "https://gmail.googleapis.com/gmail/v1/users/me/messages/{id}" - -[[tools]] -name = "gmail_modify_labels" -description = "Add or remove labels on a Gmail message." -kind = "http" -command = "https://gmail.googleapis.com/gmail/v1/users/me/messages/{id}/modify" -``` - -At runtime, `load_skills_with_config()` (`src/skills/mod.rs:78`) reads these tools into -`Vec` and injects them into the agent system prompt. The `kind = "http"` tools -are dispatched via `HttpRequestTool` (`src/tools/http_request.rs`) with the stored -Bearer token injected as the `Authorization` header (see §5). - ---- - -## 3. OAuth2 Connect Flow - -### 3.1 What the user triggers - -When the user asks to connect Gmail (via the frontend or a CLI command), the backend: - -1. Generates a **PKCE pair** (code verifier + SHA-256 challenge) — following the same - pattern as `src/auth/openai_oauth.rs:generate_pkce_state()`. -2. Generates a random CSRF `state` token (24 bytes, base64url). -3. Constructs the Google OAuth2 authorisation URL: - -``` -https://accounts.google.com/o/oauth2/v2/auth - ?response_type=code - &client_id= - &redirect_uri=/auth/callback/gmail - &scope=https://www.googleapis.com/auth/gmail.send - https://www.googleapis.com/auth/gmail.modify - https://www.googleapis.com/auth/gmail.labels - &code_challenge= - &code_challenge_method=S256 - &state= - &access_type=offline - &prompt=consent -``` - -`access_type=offline` is required to receive a `refresh_token` from Google. -`prompt=consent` ensures the refresh token is issued on every connect, not just the first. - -4. The URL is sent to the frontend. The frontend opens it in a browser or WebView. - -### 3.2 Where the redirect URI points - -The `redirect_uri` is the **tunnel public URL** with the path `/auth/callback/gmail`. - -ZeroClaw's tunnel system (`src/tunnel/mod.rs`) exposes the local gateway port via one of: - -| Provider | How public URL is obtained | -| ------------ | ---------------------------------------------------------------- | -| `cloudflare` | `cloudflared tunnel` process stdout — `src/tunnel/cloudflare.rs` | -| `ngrok` | ngrok local API `GET /api/tunnels` — `src/tunnel/ngrok.rs` | -| `tailscale` | `tailscale funnel` + hostname — `src/tunnel/tailscale.rs` | -| `custom` | user-supplied URL or stdout pattern — `src/tunnel/custom.rs` | - -The tunnel's `start(local_host, local_port)` method returns the public URL string. -This URL is what gets used as the `redirect_uri`. - -Example with Cloudflare Tunnel: - -``` -https://your-subdomain.trycloudflare.com/auth/callback/gmail -``` - -Google's OAuth2 server sends the browser to this URL after the user grants consent. - ---- - -## 4. Receiving the Authorization Code via the Tunnel - -### 4.1 The callback endpoint - -The gateway (`src/gateway/mod.rs`) runs as an Axum HTTP server. A new route needs to be -registered to receive the OAuth2 callback: - -``` -GET /auth/callback/gmail?code=&state= -``` - -This route handler must: - -1. **Validate the `state` parameter** against the CSRF token stored in memory when the - flow was initiated. Reject mismatches with `400 Bad Request`. -2. **Extract the `code`** query parameter. -3. **Exchange the code** for tokens by calling Google's token endpoint: - -``` -POST https://oauth2.googleapis.com/token - grant_type=authorization_code - code= - client_id= - client_secret= - redirect_uri=/auth/callback/gmail - code_verifier= -``` - -The exchange pattern mirrors `src/auth/openai_oauth.rs:exchange_code_for_tokens()`, -which posts a form body and parses the JSON response into `TokenSet`. - -Google returns: - -```json -{ - "access_token": "ya29.a0...", - "expires_in": 3599, - "refresh_token": "1//0g...", - "scope": "https://www.googleapis.com/auth/gmail.send ...", - "token_type": "Bearer" -} -``` - -4. The handler stores the `TokenSet` (see §4.2) and returns a success response to the - browser (an HTML page or redirect to the frontend app confirming the connection). - -### 4.2 The in-flight CSRF / PKCE state - -Before redirecting the user to Google, the pending PKCE state and CSRF token must be -stored so the callback handler can look them up. The right storage is the skill's dedicated -in-memory or short-lived file-backed state, keyed by the `state` value: - -``` -pending_oauth_states: HashMap -``` - -Entries expire after a fixed window (e.g. 10 minutes) to prevent orphaned state -accumulation. This follows the same pattern ZeroClaw already uses for pairing nonces -in `src/security/pairing.rs`. - ---- - -## 5. Token Storage - -After a successful token exchange, the tokens are persisted using the `AuthService` -and `AuthProfilesStore` from `src/auth/`. - -### 5.1 Storing the token set - -```rust -// Pseudocode — mirrors src/auth/mod.rs AuthService::store_openai_tokens() -let token_set = TokenSet { - access_token: response.access_token, - refresh_token: response.refresh_token, - id_token: None, - expires_at: Some(Utc::now() + Duration::seconds(response.expires_in)), - token_type: Some("Bearer".into()), - scope: Some(scopes.join(" ")), -}; - -auth_service - .store_oauth_tokens("gmail", "default", token_set, None, true) - .await?; -``` - -`store_oauth_tokens` calls `AuthProfilesStore::upsert_profile()`, which: - -1. Creates or updates an `AuthProfile` with `kind = AuthProfileKind::OAuth`. -2. Serialises the profile data to `~/.zeroclaw/auth-profiles.json` (file-locked via - `auth-profiles.lock` with a 10-second timeout — `src/auth/profiles.rs:16–17`). - -### 5.2 How tokens are encrypted at rest - -Before writing to disk, every secret field passes through `SecretStore::encrypt()` -(`src/security/secrets.rs`). The encryption scheme is: - -- **Algorithm**: ChaCha20-Poly1305 AEAD (256-bit key) -- **Key file**: `~/.zeroclaw/.secret_key` (permissions 0600, created on first use) -- **Format on disk**: `enc2:` -- **Config**: encryption is enabled by default; disable with `secrets.encrypt = false` - -The stored profile entry looks like: - -```json -{ - "id": "gmail:default", - "provider": "gmail", - "profile_name": "default", - "kind": "oauth", - "token_set": { - "access_token": "enc2:a1b2c3...", - "refresh_token": "enc2:d4e5f6...", - "expires_at": "2026-03-09T12:00:00Z", - "token_type": "Bearer", - "scope": "https://www.googleapis.com/auth/gmail.send ..." - }, - "created_at": "2026-03-09T11:00:00Z", - "updated_at": "2026-03-09T11:00:00Z" -} -``` - -### 5.3 Token refresh - -Google access tokens expire after 3600 seconds. Before any Gmail API call, the skill -must check `token_set.is_expiring_within(Duration::from_secs(90))` (the same 90-second -skew used for OpenAI tokens in `src/auth/mod.rs:160`). If expiring: - -``` -POST https://oauth2.googleapis.com/token - grant_type=refresh_token - refresh_token= - client_id= - client_secret= -``` - -The new `access_token` (and new `refresh_token` if Google rotates it) is written back -to the auth profile via `AuthProfilesStore::update_profile()`. - ---- - -## 6. Initial Email Sync (First 100 Emails) - -Once connected, the skill performs a one-time initial sync. This is triggered -immediately after a successful token exchange and runs as a background task. - -### 6.1 Fetch message IDs - -``` -GET https://gmail.googleapis.com/gmail/v1/users/me/messages - ?maxResults=100 - &labelIds=INBOX -Authorization: Bearer -``` - -Returns up to 100 message descriptors: `[{ "id": "...", "threadId": "..." }, ...]`. - -### 6.2 Fetch full message details - -For each message ID, fetch the full message: - -``` -GET https://gmail.googleapis.com/gmail/v1/users/me/messages/{id} - ?format=full -Authorization: Bearer -``` - -The response contains headers (From, To, Subject, Date), a snippet, and the body parts. - -In practice, to avoid 100 sequential requests, use the Gmail batch API: - -``` -POST https://www.googleapis.com/batch/gmail/v1 -Content-Type: multipart/mixed; boundary="batch_boundary" - ---batch_boundary -Content-Type: application/http -GET /gmail/v1/users/me/messages/{id1}?format=full -... -``` - -### 6.3 Storing emails in ZeroClaw memory - -Each email is stored via `memory_store` (`src/tools/memory_store.rs`) with: - -| Field | Value | -| ---------- | ----------------------------------------------------------------- | -| `key` | `gmail_msg_` | -| `content` | Formatted string: `From: ... Subject: ... Date: ... Snippet: ...` | -| `category` | `MemoryCategory::Custom("gmail")` | - -This makes every synced email searchable via `memory_recall` with queries like -`"gmail from:zeroclaw@example.com"`. - -For larger body content that exceeds a single memory entry, store the full body as a -separate entry keyed `gmail_body_` and the summary/metadata as -`gmail_msg_`. - -Example stored entries after sync: - -``` -key: gmail_msg_18de7f2a1b3c4e5d -content: From: sender@example.com - To: me@gmail.com - Subject: Project update - Date: 2026-03-08T14:32:00Z - Snippet: "Here is the latest update on the project..." - Labels: INBOX, UNREAD -category: gmail - -key: gmail_body_18de7f2a1b3c4e5d -content: (full decoded email body text) -category: gmail -``` - -### 6.4 Sync state tracking - -Store the highest-known history ID after sync so incremental sync can pick up from -where it left off: - -``` -key: gmail_sync_history_id -content: 12345678 -category: core -``` - ---- - -## 7. The `gmail_send_email` Tool - -### 7.1 Tool definition in `SKILL.toml` - -The `[[tools]]` entry from §2: - -```toml -[[tools]] -name = "gmail_send_email" -description = "Send an email via Gmail. Requires a connected Gmail account." -kind = "http" -command = "https://gmail.googleapis.com/gmail/v1/users/me/messages/send" -``` - -### 7.2 How the agent calls it - -The LLM emits a tool call: - -```json -{ - "name": "gmail_send_email", - "arguments": { - "to": "recipient@example.com", - "subject": "Hello from ZeroClaw", - "body": "This is a test email sent by the agent." - } -} -``` - -### 7.3 Tool dispatch path - -``` -Agent loop (src/agent/loop_.rs) - └── dispatches to registered tool by name - └── HttpRequestTool::execute(args) [src/tools/http_request.rs] - ├── Retrieve gmail access_token from AuthService - │ └── auth_service.get_provider_bearer_token("gmail", None) - │ └── decrypts enc2: value via SecretStore::decrypt() - ├── Build RFC 2822 message and base64url-encode it - ├── POST https://gmail.googleapis.com/gmail/v1/users/me/messages/send - │ Headers: - │ Authorization: Bearer - │ Content-Type: application/json - │ Body: - │ { "raw": "" } - └── Return ToolResult { success, output: "Message sent. ID: " } -``` - -### 7.4 Building the RFC 2822 message - -Gmail's send endpoint requires the email encoded as an RFC 2822 message, then base64url -encoded. The structure: - -``` -From: me@gmail.com -To: recipient@example.com -Subject: Hello from ZeroClaw -Content-Type: text/plain; charset="UTF-8" -MIME-Version: 1.0 - -This is a test email sent by the agent. -``` - -Then the entire string is base64url-encoded (no padding) and placed in the `raw` field: - -```json -{ "raw": "RnJvbTogbWVAZ21haWwuY29tCi..." } -``` - -### 7.5 Security enforcement - -Before executing any outbound HTTP call, the `SecurityPolicy` is consulted: - -```rust -// src/security/policy.rs -security.enforce_tool_operation(ToolOperation::Act, "gmail_send_email") -``` - -`ToolOperation::Act` is the category for write/side-effect operations. If the agent is -running in `ReadOnly` or `Supervised` mode, this call fails with an explicit error before -any network request is made. - -The `HttpRequestTool` additionally enforces the `http.allowed_domains` allowlist. For -Gmail tools, `googleapis.com` must be present in that list: - -```toml -# config.toml -[http] -enabled = true -allowed_domains = ["googleapis.com"] -``` - -### 7.6 Reply-to and threading - -To send a reply within an existing thread, add the Gmail `threadId` to the request body: - -```json -{ "raw": "", "threadId": "18de7f2a1b3c4e5d" } -``` - -The RFC 2822 message must include `In-Reply-To: ` and -`References: ` headers for proper threading. - ---- - -## 8. Full End-to-End Sequence - -``` -Frontend / User - │ - │ 1. "Connect Gmail" - ▼ -ZeroClaw Backend - │ 2. Generate PKCE + CSRF state - │ 3. Build Google OAuth2 authorize URL with tunnel redirect_uri - │ 4. Return URL to frontend - │ - ▼ -Browser (user) - │ 5. User visits URL, grants consent in Google - │ - ▼ -Google OAuth2 - │ 6. Browser redirected to /auth/callback/gmail?code=XXX&state=YYY - │ - ▼ -Tunnel (cloudflare / ngrok / tailscale) - │ 7. Proxies HTTPS request to local gateway port - │ - ▼ -Gateway (src/gateway/mod.rs) — GET /auth/callback/gmail - │ 8. Validate CSRF state - │ 9. Exchange code → TokenSet via POST https://oauth2.googleapis.com/token - │ 10. Store TokenSet in AuthProfilesStore (encrypted, "gmail:default") - │ 11. Return success page/redirect to frontend - │ - ▼ -Background task — initial sync - │ 12. GET /gmail/v1/users/me/messages?maxResults=100&labelIds=INBOX - │ 13. Batch-fetch 100 full messages - │ 14. memory_store each email under key=gmail_msg_, category=gmail - │ 15. memory_store gmail_sync_history_id = - │ - ▼ -Agent (subsequent interactions) - │ 16. LLM generates tool call: gmail_send_email { to, subject, body } - │ 17. Agent loop dispatches to HttpRequestTool - │ 18. HttpRequestTool retrieves + decrypts access_token from AuthService - │ 19. Refreshes token if expiring within 90 seconds - │ 20. POST https://gmail.googleapis.com/gmail/v1/users/me/messages/send - │ 21. Returns ToolResult to agent - ▼ -Done -``` - ---- - -## 9. Configuration Reference - -All Gmail skill config lives in `config.toml`: - -```toml -# Enable HTTP request tool with Google API access -[http] -enabled = true -allowed_domains = ["googleapis.com", "oauth2.googleapis.com"] -timeout_secs = 30 -max_response_size = 524288 # 512KB — enough for email batch responses - -# Tunnel for OAuth callback (pick one) -[tunnel] -provider = "cloudflare" -[tunnel.cloudflare] -token = "your-cloudflare-tunnel-token" - -# Or ngrok: -# [tunnel] -# provider = "ngrok" -# [tunnel.ngrok] -# auth_token = "your-ngrok-token" - -# Google OAuth2 app credentials (stored encrypted) -# Set via environment or onboard wizard — never commit raw values -[integrations.gmail] -client_id = "enc2:..." -client_secret = "enc2:..." -``` - -The Google credentials (`client_id`, `client_secret`) are encrypted by `SecretStore` -before being written to `config.toml` — the same ChaCha20-Poly1305 scheme used for all -secrets (`src/security/secrets.rs`). - ---- - -## 10. Key Source Files - -| File | Role | -| ---------------------------- | ----------------------------------------------------------------- | -| `src/skills/mod.rs` | Skill loading, `SkillTool` struct, `load_skills_with_config()` | -| `src/auth/mod.rs` | `AuthService` — store/retrieve/refresh OAuth tokens | -| `src/auth/profiles.rs` | `AuthProfile`, `TokenSet`, `AuthProfilesStore` — JSON persistence | -| `src/auth/openai_oauth.rs` | PKCE generation, code exchange, refresh — reference pattern | -| `src/security/secrets.rs` | `SecretStore` — ChaCha20-Poly1305 encrypt/decrypt | -| `src/tunnel/mod.rs` | `Tunnel` trait + factory — public URL for OAuth redirect | -| `src/tunnel/cloudflare.rs` | Cloudflare Tunnel implementation | -| `src/tunnel/ngrok.rs` | ngrok implementation | -| `src/gateway/mod.rs` | Axum HTTP gateway — where `/auth/callback/gmail` is registered | -| `src/tools/http_request.rs` | `HttpRequestTool` — dispatches Gmail API calls | -| `src/tools/memory_store.rs` | `MemoryStoreTool` — stores synced emails | -| `src/tools/memory_recall.rs` | `MemoryRecallTool` — searches synced emails | -| `src/security/policy.rs` | `SecurityPolicy` — enforces `ToolOperation::Act` guards | -| `docs/config-reference.md` | Full config schema including `[http]`, `[tunnel]` | - ---- - -## 11. Security Notes - -- The OAuth2 `redirect_uri` **must be the tunnel URL**. It cannot be `localhost` in a - remote/mobile scenario. Google validates the exact URI registered in the Google Cloud - Console; register it as `https:///auth/callback/gmail`. -- The CSRF `state` token must be validated on every callback. Reject mismatched or - missing state with `400 Bad Request` before touching any tokens. -- The PKCE verifier must be destroyed after a successful or failed exchange — never - persist it beyond the in-flight flow. -- `client_secret` must never appear in plaintext in `config.toml`, logs, or agent - tool output. Encrypt it via the secret store and redact it in observability output. -- `googleapis.com` must be explicitly present in `http.allowed_domains` — the - `HttpRequestTool` enforces this allowlist before every request - (`src/tools/http_request.rs`). -- Token refresh runs under the same per-profile Tokio mutex used for OpenAI tokens - (`src/auth/mod.rs:287`) to prevent double-refresh races. diff --git a/docs/teams-api-reference.md b/docs/teams-api-reference.md deleted file mode 100644 index 9c194df282..0000000000 --- a/docs/teams-api-reference.md +++ /dev/null @@ -1,530 +0,0 @@ -# AlphaHuman Teams API Reference - -Complete reference for all teams-related API endpoints in the AlphaHuman platform. - -**Base URL**: `https://api.alphahuman.xyz` -**Authentication**: Bearer JWT token required for all endpoints -**Content-Type**: `application/json` - ---- - -## Core Team Management - -### 1. Create Team - -**POST** `/teams` - -Creates a new team with optional encryption. - -**Request Body:** - -```json -{ "name": "string (required)", "magicWord": "string (optional)" } -``` - -**Response Schema:** - -```json -{ - "id": "string", - "name": "string", - "slug": "string", - "magicWord": "string | null", - "createdBy": "string", - "isPersonal": "boolean", - "subscription": { - "hasActiveSubscription": "boolean", - "plan": "FREE|BASIC|PRO", - "planExpiry": "date-time | null", - "stripeCustomerId": "string | null" - }, - "usage": { - "weeklyBudgetUsd": "number", - "spentThisWeekUsd": "number", - "weekStartDate": "date-time" - }, - "inviteCode": "string | null", - "maxMembers": "number", - "createdAt": "date-time", - "updatedAt": "date-time" -} -``` - -**Status Codes:** - -- `200` - Team created successfully -- `401` - Unauthorized - ---- - -### 2. List Teams - -**GET** `/teams` - -Retrieves all teams the authenticated user belongs to. - -**Response Schema:** - -```json -{ - "success": true, - "data": [ - { - "team": { - "id": "string", - "name": "string", - "slug": "string", - "isPersonal": "boolean", - "subscription": { "hasActiveSubscription": "boolean", "plan": "FREE|BASIC|PRO" } - }, - "role": "admin|billing_manager|member" - } - ] -} -``` - -**Status Codes:** - -- `200` - Success -- `401` - Unauthorized - ---- - -### 3. Get Team Details - -**GET** `/teams/{teamId}` - -Retrieves detailed information for a specific team. - -**Parameters:** - -- `teamId` (path, required) - Team identifier - -**Response Schema:** - -```json -{ - "id": "string", - "name": "string", - "slug": "string", - "magicWord": "string | null", - "createdBy": "string", - "isPersonal": "boolean", - "subscription": { - "hasActiveSubscription": "boolean", - "plan": "FREE|BASIC|PRO", - "planExpiry": "date-time | null", - "stripeCustomerId": "string | null" - }, - "usage": { - "weeklyBudgetUsd": "number", - "spentThisWeekUsd": "number", - "weekStartDate": "date-time" - }, - "inviteCode": "string | null", - "maxMembers": "number", - "createdAt": "date-time", - "updatedAt": "date-time" -} -``` - -**Status Codes:** - -- `200` - Success -- `403` - Not a member of the team -- `404` - Team not found - ---- - -### 4. Update Team Settings - -**PUT** `/teams/{teamId}` - -Updates team settings. **Admin only**. - -**Parameters:** - -- `teamId` (path, required) - Team identifier - -**Request Body:** - -```json -{ "name": "string (optional)", "maxMembers": "number (optional)" } -``` - -**Status Codes:** - -- `200` - Team updated successfully -- `403` - Only admins can update team settings - ---- - -### 5. Delete Team - -**DELETE** `/teams/{teamId}` - -Deletes a team. **Admin only, non-personal teams only**. - -**Parameters:** - -- `teamId` (path, required) - Team identifier - -**Status Codes:** - -- `200` - Team deleted successfully -- `400` - Cannot delete a personal team -- `403` - Only admins can delete a team - ---- - -### 6. Switch Active Team - -**POST** `/teams/{teamId}/switch` - -Changes the user's active team. - -**Parameters:** - -- `teamId` (path, required) - Team identifier - -**Status Codes:** - -- `200` - Successfully switched active team -- `403` - Not a member of the specified team - ---- - -## Team Member Management - -### 7. List Team Members - -**GET** `/teams/{teamId}/members` - -Lists all members of a team. - -**Parameters:** - -- `teamId` (path, required) - Team identifier - -**Response Schema:** - -```json -{ - "success": true, - "data": [{ "user": "string", "role": "admin|billing_manager|member", "joinedAt": "date-time" }] -} -``` - -**Status Codes:** - -- `200` - Success -- `403` - Not a member of this team - ---- - -### 8. Remove Team Member - -**DELETE** `/teams/{teamId}/members/{userId}` - -Removes a member from the team. **Admin only**. - -**Parameters:** - -- `teamId` (path, required) - Team identifier -- `userId` (path, required) - User identifier to remove - -**Status Codes:** - -- `200` - Member removed -- `403` - Only admins can remove members - ---- - -### 9. Change Member Role - -**PUT** `/teams/{teamId}/members/{userId}/role` - -Changes a member's role within the team. **Admin only**. - -**Parameters:** - -- `teamId` (path, required) - Team identifier -- `userId` (path, required) - User identifier - -**Request Body:** - -```json -{ "role": "admin|billing_manager|member" } -``` - -**Status Codes:** - -- `200` - Role updated -- `403` - Only admins can change member roles - ---- - -### 10. Leave Team - -**POST** `/teams/{teamId}/leave` - -Allows a user to leave a team. - -**Parameters:** - -- `teamId` (path, required) - Team identifier - -**Status Codes:** - -- `200` - Successfully left the team -- `400` - Cannot leave as the only admin - ---- - -## Team Invite Management - -### 11. Create Team Invite - -**POST** `/teams/{teamId}/invites` - -Creates a new invite code for the team. - -**Parameters:** - -- `teamId` (path, required) - Team identifier - -**Request Body (Optional):** - -```json -{ "maxUses": "number (default: 1)", "expiresInDays": "number (default: 7)" } -``` - -**Response Schema:** - -```json -{ - "success": true, - "data": { "code": "string (e.g., T-1A2B3C4D5E6F)", "expiresAt": "date-time", "maxUses": "number" } -} -``` - -**Status Codes:** - -- `200` - Invite created successfully -- `403` - Not a team member - ---- - -### 12. List Team Invites - -**GET** `/teams/{teamId}/invites` - -Lists all invites for a team. - -**Parameters:** - -- `teamId` (path, required) - Team identifier - -**Status Codes:** - -- `200` - Success -- `403` - User is not a member of the team - ---- - -### 13. Revoke Team Invite - -**DELETE** `/teams/{teamId}/invites/{inviteId}` - -Revokes a team invite. **Admin or invite creator only**. - -**Parameters:** - -- `teamId` (path, required) - Team identifier -- `inviteId` (path, required) - Invite identifier - -**Status Codes:** - -- `200` - Invite successfully revoked -- `403` - Only admins or invite creator can revoke -- `404` - Invite does not exist - ---- - -### 14. Join Team - -**POST** `/teams/join` - -Joins a team using an invite code. - -**Request Body:** - -```json -{ "code": "string (required, e.g., T-1A2B3C4D5E6F)" } -``` - -**Response Schema:** - -```json -{ "success": true, "data": { "team": "string", "membership": "string" } } -``` - -**Status Codes:** - -- `200` - Joined the team successfully -- `400` - Invite expired, max uses reached, or already a team member -- `404` - Invite code not found - ---- - -## Team Billing Management - -### 15. Purchase Team Subscription - -**POST** `/teams/{teamId}/billing/purchase` - -Purchases a subscription plan for the team. **Admin or billing manager only**. - -**Parameters:** - -- `teamId` (path, required) - Team identifier - -**Request Body:** - -```json -{ - "plan": "BASIC_MONTHLY|BASIC_YEARLY|PRO_MONTHLY|PRO_YEARLY", - "successUrl": "string (optional)", - "cancelUrl": "string (optional)" -} -``` - -**Status Codes:** - -- `200` - Checkout session created successfully -- `403` - Only admins or billing managers can purchase plans - ---- - -### 16. Get Team Subscription Plan - -**GET** `/teams/{teamId}/billing/plan` - -Retrieves the team's current subscription information. - -**Parameters:** - -- `teamId` (path, required) - Team identifier - -**Response Schema:** - -```json -{ - "success": true, - "data": { - "plan": "FREE|BASIC|PRO", - "hasActiveSubscription": "boolean", - "planExpiry": "date-time | null" - } -} -``` - -**Status Codes:** - -- `200` - Success -- `401` - Unauthorized - ---- - -### 17. Create Billing Portal Session - -**POST** `/teams/{teamId}/billing/portal` - -Creates a Stripe billing portal session for subscription management. **Admin or billing manager only**. - -**Parameters:** - -- `teamId` (path, required) - Team identifier - -**Request Body (Optional):** - -```json -{ "returnUrl": "string" } -``` - -**Response Schema:** - -```json -{ "success": true, "data": { "url": "string" } } -``` - -**Status Codes:** - -- `200` - Portal session created successfully -- `403` - Only admins or billing managers can create portal session - ---- - -## Team Roles - -### Role Hierarchy - -1. **admin** - Full team management permissions -2. **billing_manager** - Billing and subscription management -3. **member** - Basic team member access - -### Permission Matrix - -| Action | Admin | Billing Manager | Member | -| -------------------- | ----- | --------------- | ------ | -| View team details | ✅ | ✅ | ✅ | -| Update team settings | ✅ | ❌ | ❌ | -| Delete team | ✅ | ❌ | ❌ | -| Add/remove members | ✅ | ❌ | ❌ | -| Change member roles | ✅ | ❌ | ❌ | -| Create invites | ✅ | ✅ | ✅ | -| Manage billing | ✅ | ✅ | ❌ | -| Leave team | ✅\* | ✅ | ✅ | - -\*Admin cannot leave if they are the only admin - ---- - -## Team Plans & Limits - -### Plan Types - -- **FREE** - Basic team functionality -- **BASIC** - Enhanced features and limits -- **PRO** - Full feature set and highest limits - -### Usage Tracking - -Teams have usage limits tracked through: - -- `weeklyBudgetUsd` - Weekly spending budget -- `spentThisWeekUsd` - Current week spending -- `weekStartDate` - When the current week started -- `maxMembers` - Maximum team size - ---- - -## Error Handling - -### Common Error Responses - -```json -{ "success": false, "error": "Error message description" } -``` - -### Authentication - -All endpoints require a Bearer JWT token: - -``` -Authorization: Bearer -``` - -### Rate Limiting - -Standard API rate limits apply to all endpoints. Refer to main API documentation for specific limits. diff --git a/docs/the-tet.png b/docs/the-tet.png deleted file mode 100644 index cb5ace1249..0000000000 Binary files a/docs/the-tet.png and /dev/null differ