# Custom Distributions of goose > **Tip:** This is sometimes referred to as "white labelling" — creating a branded or tailored version of an open source project for your organization. This guide explains how to create custom distributions of goose tailored to your organization's needs—whether that's preconfigured models, custom tools, branded interfaces, or entirely new user experiences. ## Overview goose's architecture is designed for extensibility. Organizations can create "remixed" versions that: - **Preconfigure AI providers**: Ship with a specific model (local or cloud) and API credentials - **Bundle custom tools**: Include proprietary extensions for internal data sources - **Customize the experience**: Modify branding, UI, and default behaviors - **Target specific audiences**: Create specialized versions for developers, legal teams, designers, etc. ## Architecture at a Glance ``` ┌─────────────────────────────────────────────────────────────────┐ │ User Interfaces │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │ │ │ CLI │ │ Desktop │ │ Your Custom UI │ │ │ │ (goose-cli)│ │ (Electron) │ │ (web, mobile, etc.) │ │ │ └──────┬──────┘ └──────┬──────┘ └────────────┬────────────┘ │ └─────────┼────────────────┼──────────────────────┼───────────────┘ │ │ │ ▼ ▼ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ goose-server (goosed) │ │ REST API for all goose functionality │ └─────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ Core (goose crate) │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │ │ │ Providers │ │ Extensions │ │ Config & Recipes │ │ │ │ (AI models)│ │ (MCP tools)│ │ (behavior & defaults) │ │ │ └─────────────┘ └─────────────┘ └─────────────────────────┘ │ └─────────────────────────────────────────────────────────────────┘ ``` ## Key Customization Points | What You Want | Where to Look | Complexity | |---------------|---------------|------------| | Preconfigure a model/provider | `config.yaml`, `init-config.yaml`, environment variables | Low | | Add custom AI providers | `crates/goose/src/providers/declarative/` | Low | | Bundle custom MCP extensions | `config.yaml` extensions section, `ui/desktop/src/built-in-extensions.json`, `ui/desktop/src/components/settings/extensions/bundled-extensions.json` | Medium | | Modify system prompts | `crates/goose/src/prompts/` | Low | | Customize desktop branding | `ui/desktop/` (icons, names, colors) | Medium | | Build a new UI (web, mobile) | Integrate with `goose-server` REST API | High | | Create guided workflows | Recipes (YAML-based task definitions) | Low | | Build complex multi-step workflows | Recipes with sub-recipes and subagents | Medium | ## Getting Started ### 1. Fork and Clone ```bash git clone https://github.com/YOUR_ORG/goose.git cd goose ``` ### 2. Choose Your Customization Strategy - **Configuration-only**: Modify config files and environment variables (no code changes) - **Extension-based**: Add custom MCP servers for your tools (minimal core changes) - **Deep customization**: Modify core behavior, UI, or add new providers ### 3. Build and Distribute See [BUILDING_LINUX.md](BUILDING_LINUX.md) and [ui/desktop/README.md](ui/desktop/README.md) for platform-specific build instructions. ## Important Considerations ### Licensing goose is licensed under Apache License 2.0 (ASL v2). Custom distributions must: - Include the original license and copyright notices - Clearly indicate any modifications made - Not use "Goose" trademarks in ways that imply official endorsement For detailed guidance on ASL v2 compliance, see the [Apache License FAQ](https://www.apache.org/foundation/license-faq.html). ### Contributing Back While you're free to maintain private forks, contributing improvements upstream benefits everyone—including your distribution. Private forks that diverge significantly become expensive to maintain and miss out on security updates and new features. Consider upstreaming generic improvements while keeping only organization-specific customizations private. ### Telemetry goose includes optional telemetry (via PostHog) to help improve the project. For custom distributions, you can: - **Disable telemetry**: Set `GOOSE_DISABLE_TELEMETRY=1` - **Use your own instance**: Modify `crates/goose/src/posthog.rs` to point to your PostHog instance ### Staying Current To benefit from upstream improvements: 1. Regularly sync your fork with the main repository 2. Keep customizations isolated (config files, separate extension repos) when possible 3. Use recipes for workflow customization rather than code changes 4. Subscribe to release announcements for breaking changes --- # Appendix: Custom Distribution Scenarios ## A. Preconfigured Local Model Distribution **Goal**: Ship goose preconfigured to use a local Ollama model, requiring no API keys. ### Steps 1. **Create an init-config.yaml** in your distribution root: ```yaml # init-config.yaml - Applied on first run if no config exists GOOSE_PROVIDER: ollama GOOSE_MODEL: qwen3-coder:latest ``` 2. **Set environment defaults** in your launcher script or packaging: ```bash export GOOSE_PROVIDER=ollama export GOOSE_MODEL=qwen3-coder:latest export OLLAMA_HOST=http://localhost:11434 # Or your hosted instance ``` 3. **Optionally hide provider selection** in the UI by modifying `ui/desktop/src/` components. ### Technical Details - Provider configuration: `crates/goose/src/config/base.rs` - Ollama provider implementation: `crates/goose/src/providers/ollama.rs` - Config precedence: Environment variables → config.yaml → defaults --- ## B. Corporate Distribution with Managed API Keys **Goal**: Distribute goose internally with pre-provisioned API keys for a frontier model. ### Steps 1. **Store API keys securely** using goose's secret management: ```yaml # config.yaml (distributed with your package) GOOSE_PROVIDER: anthropic GOOSE_MODEL: claude-sonnet-4-20250514 ``` 2. **Inject secrets at install time** or via your MDM/configuration management: ```bash # Secrets are stored in system keyring or ~/.config/goose/secrets.yaml # if GOOSE_DISABLE_KEYRING=1 goose configure set-secret ANTHROPIC_API_KEY "your-corporate-key" ``` 3. **Lock down provider changes** (optional) by modifying the settings UI or using a recipe that enforces the provider. ### Technical Details - Secret storage: `crates/goose/src/config/base.rs` (SecretStorage enum) - Keyring integration: Uses system keyring by default, file-based fallback available - Config file location: `~/.config/goose/config.yaml` --- ## C. Custom Tools for Internal Data Sources **Goal**: Add MCP extensions that connect to your data lake, internal APIs, or proprietary systems. ### Steps 1. **Create your MCP server** following the [MCP specification](https://modelcontextprotocol.io/): ```python # Example: internal_data_mcp.py from mcp.server import Server from mcp.types import Tool server = Server("internal-data") @server.tool() async def query_data_lake(query: str) -> str: """Query the corporate data lake.""" # Your implementation here return results ``` 2. **Bundle as a built-in extension** by adding to either: - `ui/desktop/src/built-in-extensions.json` (core built-ins surfaced in extension UI) - `ui/desktop/src/components/settings/extensions/bundled-extensions.json` (bundled extension catalog in Settings) Example: ```json { "id": "internal-data", "name": "Internal Data Lake", "description": "Query corporate data sources", "enabled": true, "type": "stdio", "cmd": "python", "args": ["/path/to/internal_data_mcp.py"], "env_keys": ["INTERNAL_DATA_API_KEY"], "timeout": 300 } ``` 3. **Or distribute as a recipe** that enables the extension: ```yaml # data-analyst.yaml title: Data Analyst Assistant description: goose configured for data analysis instructions: | You have access to the corporate data lake. Help users query and analyze data. extensions: - type: stdio name: internal-data cmd: python args: ["/opt/corp-goose/internal_data_mcp.py"] description: Corporate data lake access ``` ### Technical Details - Extension types: `crates/goose/src/agents/extension.rs` (ExtensionConfig enum) - Built-in MCP servers: `crates/goose-mcp/` - Extension loading: `crates/goose/src/agents/extension_manager.rs` --- ## D. Custom Branding and UI **Goal**: Rebrand the desktop application with your organization's identity. ### Steps 1. **Replace visual assets** in `ui/desktop/src/images/`: - `icon.png`, `icon.ico`, `icon.icns` - Application icons - Update splash screens and logos as needed 2. **Modify application metadata** in `ui/desktop/forge.config.ts`: ```typescript // forge.config.ts module.exports = { packagerConfig: { name: 'YourCompany AI Assistant', executableName: 'yourcompany-ai', icon: 'src/images/your-icon', // ... }, // ... }; ``` 3. **Update the system prompt** to reflect your branding in `crates/goose/src/prompts/system.md`: ```markdown You are an AI assistant called [YourName], created by [YourCompany]. ... ``` 4. **Customize UI components** in `ui/desktop/src/` (React/TypeScript): - Color schemes in CSS/Tailwind config - Component text and labels - Feature visibility 5. **Align packaging and updater names** when rebranding: - Update static branding metadata in `ui/desktop/package.json` (`productName`, description) and Linux desktop templates (`ui/desktop/forge.deb.desktop`, `ui/desktop/forge.rpm.desktop`) - Set build/release environment variables consistently: - `GITHUB_OWNER` and `GITHUB_REPO` for publisher + updater repository lookup - `GOOSE_BUNDLE_NAME` for bundle/debug scripts and updater asset naming (defaults to `Goose`) Example: ```bash export GITHUB_OWNER="your-org" export GITHUB_REPO="your-goose-fork" export GOOSE_BUNDLE_NAME="InsightStream-goose" ``` 6. **Use this branding consistency checklist** before release: - Application metadata (`forge.config.ts`, `package.json`, `index.html`) uses your distro name - Release artifact names and updater lookup names are consistent - Desktop launchers (Linux `.desktop` templates) point to the same executable name produced by packaging ### Technical Details - Electron config: `ui/desktop/forge.config.ts` - UI entry point: `ui/desktop/src/renderer.tsx` - System prompts: `crates/goose/src/prompts/` --- ## E. Building a New Interface (Web, Mobile, etc.) **Goal**: Create an entirely new frontend while leveraging goose's backend. goose provides two integration options for building custom UIs: ### Option 1: REST API (goose-server) Use goose-server for HTTP-based integrations (web apps, simple clients): ```bash # Start the server ./target/release/goosed # API available at http://localhost:3000 ``` **Reference the OpenAPI spec** at `ui/desktop/openapi.json` for available endpoints: - Session management - Message streaming - Extension control - Configuration **Key endpoints** for a minimal integration: ``` POST /sessions # Create a new session POST /sessions/{id}/messages # Send a message (streaming response) GET /sessions/{id} # Get session state GET /extensions # List available extensions POST /extensions/{name}/enable # Enable an extension ``` **Handle streaming responses** - goose uses Server-Sent Events (SSE) for real-time responses. ### Option 2: Agent Client Protocol (ACP) For richer integrations (IDEs, desktop apps, embedded agents), use the **Agent Client Protocol (ACP)**—a standardized JSON-RPC protocol for AI agent communication over stdio or other transports. ACP provides: - **Bidirectional communication**: Agents can request permissions, stream updates, and receive cancellations - **Rich tool call handling**: Detailed status updates, locations, and content for each tool invocation - **Session management**: Create, load, and resume sessions with full conversation history - **MCP server integration**: Dynamically add MCP servers to sessions **Start goose as an ACP agent**: ```bash # Run goose as an ACP server on stdio goose acp --with-builtin developer,memory # Or programmatically cargo run -p goose-cli -- acp --with-builtin developer ``` **Key ACP methods**: | Method | Description | |--------|-------------| | `initialize` | Establish connection and exchange capabilities | | `session/new` | Create a new session with optional MCP servers | | `session/load` | Resume an existing session by ID | | `session/prompt` | Send a prompt and receive streaming responses | | `session/cancel` | Cancel an in-progress prompt | **Example: Python ACP client** (see `test_acp_client.py` for a complete example): ```python import subprocess import json class AcpClient: def __init__(self): self.process = subprocess.Popen( ['goose', 'acp', '--with-builtin', 'developer'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, text=True ) def send_request(self, method, params=None): request = {"jsonrpc": "2.0", "method": method, "id": 1} if params: request["params"] = params self.process.stdin.write(json.dumps(request) + "\n") self.process.stdin.flush() return json.loads(self.process.stdout.readline()) # Initialize and create session client = AcpClient() client.send_request("initialize", {"protocolVersion": "2025-01-01"}) session = client.send_request("session/new", {"cwd": "/path/to/project"}) # Send a prompt (responses stream as notifications) client.send_request("session/prompt", { "sessionId": session["result"]["sessionId"], "prompt": [{"type": "text", "text": "List files in this directory"}] }) ``` **ACP notifications** (sent from agent to client): - `session/notification` with `agentMessageChunk` - Streaming text responses - `session/notification` with `toolCall` - Tool invocation started - `session/notification` with `toolCallUpdate` - Tool status/result updates - `requestPermission` - Agent requests user confirmation for sensitive operations For the full ACP specification, see the [Agent Client Protocol documentation](https://github.com/anthropics/anthropic-cookbook/tree/main/misc/agent_client_protocol). ### Technical Details **REST API (goose-server)**: - Server implementation: `crates/goose-server/src/routes/` - OpenAPI generation: `just generate-openapi` - API client example: `ui/desktop/src/api/` (generated TypeScript client) **ACP**: - ACP server implementation: `crates/goose-acp/src/server.rs` - CLI integration: `crates/goose-cli/src/cli.rs` (Command::Acp) - Protocol library: `sacp` crate (Rust implementation of ACP) - Test client example: `test_acp_client.py` --- ## F. Audience-Specific Distributions (Legal, Design, etc.) **Goal**: Create a version of goose tailored for a specific professional audience. ### Steps 1. **Create a specialized recipe** that defines the experience: ```yaml # legal-assistant.yaml title: Legal Research Assistant description: AI assistant for legal professionals instructions: | You are a legal research assistant. You help lawyers and paralegals with: - Case law research - Document review and summarization - Contract analysis - Legal writing assistance Always cite sources. Flag when you're uncertain. Never provide actual legal advice. extensions: - type: builtin name: developer description: File and document tools - type: stdio name: legal-database cmd: python args: ["/opt/legal-goose/legal_db_mcp.py"] description: Legal database search activities: - "Research case law on..." - "Summarize this contract..." - "Find precedents for..." settings: goose_provider: anthropic goose_model: claude-sonnet-4-20250514 ``` 2. **Customize the UI** to show only relevant features and use domain-appropriate language. 3. **Bundle domain-specific extensions** for specialized data sources (legal databases, design tools, etc.). ### Technical Details - Recipe format: `crates/goose/src/recipe/mod.rs` - Recipe loading: `crates/goose/src/recipe/local_recipes.rs` - Activity suggestions: Shown in UI as quick-start prompts --- ## G. Adding a Custom AI Provider **Goal**: Add support for a new AI provider or your self-hosted model endpoint. ### Option 1: Declarative Provider (No Code) Create a JSON file in `~/.config/goose/custom_providers/` or bundle in your distribution: ```json { "name": "my_provider", "engine": "openai", "display_name": "My Custom Provider", "description": "Our internal LLM endpoint", "api_key_env": "MY_PROVIDER_API_KEY", "base_url": "https://llm.internal.company.com/v1/chat/completions", "models": [ { "name": "company-llm-v1", "context_limit": 32768 } ], "supports_streaming": true, "requires_auth": true } ``` Supported engines: `openai`, `anthropic`, `ollama` ### Option 2: Custom Provider (Code) For providers with unique APIs, implement the Provider trait: 1. Create a new file in `crates/goose/src/providers/` 2. Implement the `Provider` trait from `base.rs` 3. Register in `crates/goose/src/providers/factory.rs` ### Technical Details - Declarative providers: `crates/goose/src/config/declarative_providers.rs` - Provider trait: `crates/goose/src/providers/base.rs` - Provider registration: `crates/goose/src/providers/factory.rs` - Example providers: `crates/goose/src/providers/declarative/*.json` --- ## H. Preconfigured Workflows with Recipes **Goal**: Create standardized, repeatable workflows that users can run with minimal setup. Recipes are YAML files that define complete goose experiences—instructions, extensions, parameters, and prompts bundled together. They're ideal for custom distributions because they require no code changes and can be distributed as simple files. ### Basic Recipe Structure ```yaml version: 1.0.0 title: Daily Standup Report Generator description: Generates standup reports from GitHub activity # Parameters users can customize at runtime parameters: - key: github_repo input_type: string requirement: required description: "GitHub repository (e.g., 'owner/repo')" - key: time_period input_type: select requirement: optional default: "24h" options: ["24h", "48h", "week"] description: "Time period to analyze" # System instructions for the AI instructions: | You are a standup report generator. Fetch PR and issue data from GitHub, analyze activity, and generate a formatted report. Always save reports to ./standup/standup-{date}.md # Extensions this recipe needs extensions: - type: builtin name: developer description: File operations - type: stdio name: github cmd: uvx args: ["github-mcp-server"] description: GitHub API access # Quick-start suggestions shown in UI activities: - "Generate today's standup report" - "Summarize this week's PRs" # Initial prompt with parameter substitution prompt: | Generate a standup report for {{ github_repo }} covering the last {{ time_period }}. ``` ### Recipe Parameter Types | Type | Description | Use Case | |------|-------------|----------| | `string` | Free-form text input | Names, paths, queries | | `number` | Numeric input | Counts, limits | | `boolean` | True/false toggle | Feature flags | | `date` | Date picker | Time-based filters | | `file` | File path (content imported) | Document processing | | `select` | Dropdown from options | Predefined choices | ### Distributing Recipes 1. **Bundle with your distribution** in a known location 2. **Share via URL** - users can import recipes from URLs 3. **Create a recipe library** - a directory of recipes for different use cases ### Technical Details - Recipe schema: `crates/goose/src/recipe/mod.rs` - Parameter handling: `crates/goose/src/recipe/template_recipe.rs` - Recipe validation: `crates/goose/src/recipe/validate_recipe.rs` --- ## I. Complex Workflows with Sub-Recipes and Subagents **Goal**: Build sophisticated multi-step workflows that orchestrate multiple specialized tasks. For complex workflows, goose supports two powerful composition mechanisms: 1. **Sub-recipes**: Predefined recipe templates that can be invoked by name 2. **Subagents**: Independent AI agents spawned to handle specific tasks ### Sub-Recipes: Predefined Task Templates Sub-recipes let you define reusable workflow components that the main recipe can invoke: ```yaml version: 1.0.0 title: Implementation Planner description: Creates detailed implementation plans with research instructions: | Create implementation plans through research and iteration. Use sub-recipes to delegate specialized research tasks. # Define available sub-recipes sub_recipes: - name: "find_files" path: "./subrecipes/codebase-locator.yaml" description: "Locate relevant files in the codebase" - name: "analyze_code" path: "./subrecipes/code-analyzer.yaml" description: "Analyze code structure and patterns" - name: "find_patterns" path: "./subrecipes/pattern-finder.yaml" # Pre-fill some parameters values: search_depth: "3" include_tests: "true" extensions: - type: builtin name: developer prompt: | Create an implementation plan for the requested feature. Use the available sub-recipes to research the codebase: - find_files: Locate relevant source files - analyze_code: Understand current implementation - find_patterns: Find similar features to model after ``` The AI can then invoke these sub-recipes using the `subagent` tool: ``` subagent(subrecipe: "find_files", parameters: {"search_term": "authentication"}) ``` ### Subagents: Dynamic Task Delegation Subagents are independent AI instances that run with their own context. They're useful for: - **Parallel execution**: Multiple tasks running simultaneously - **Context isolation**: Preventing context window overflow - **Specialized tasks**: Different model/settings per task #### Ad-hoc Subagents Create subagents on-the-fly with custom instructions: ```yaml prompt: | To complete this task: 1. Spawn a subagent to analyze the frontend code: subagent(instructions: "Analyze all React components in src/components/ and list their props and state management patterns") 2. Spawn another subagent for the backend: subagent(instructions: "Document all API endpoints in src/api/ including their request/response schemas") 3. Synthesize findings from both subagents into a unified report. ``` #### Parallel Subagent Execution Multiple subagent calls in the same message execute in parallel: ```yaml prompt: | Run these analyses in parallel by making all subagent calls at once: subagent(instructions: "Count lines of code by language") subagent(instructions: "Find all TODO comments") subagent(instructions: "List external dependencies") Then combine the results into a codebase health report. ``` #### Subagent Settings Override Customize model, provider, or behavior per subagent: ```yaml prompt: | Use a faster model for simple tasks: subagent( instructions: "List all files modified in the last week", settings: { model: "gpt-4o-mini", max_turns: 3 } ) Use the full model for complex analysis: subagent( instructions: "Review this code for security vulnerabilities", settings: { model: "claude-sonnet-4-20250514", temperature: 0.1 } ) ``` #### Extension Scoping Limit which extensions a subagent can access: ```yaml prompt: | Create a sandboxed subagent with only file reading capabilities: subagent( instructions: "Analyze the README files in this project", extensions: ["developer"] # Only developer extension, no network access ) ``` ### Example: Multi-Stage Code Review Workflow ```yaml version: 1.0.0 title: Comprehensive Code Review description: Multi-stage code review with parallel analysis sub_recipes: - name: "security_scan" path: "./subrecipes/security-scanner.yaml" sequential_when_repeated: true # Don't run multiple security scans in parallel - name: "style_check" path: "./subrecipes/style-checker.yaml" - name: "test_coverage" path: "./subrecipes/coverage-analyzer.yaml" parameters: - key: pr_number input_type: number requirement: required description: "Pull request number to review" - key: review_depth input_type: select requirement: optional default: "standard" options: ["quick", "standard", "thorough"] instructions: | Perform a comprehensive code review using specialized sub-recipes. ## Review Process ### Phase 1: Parallel Analysis Run these checks simultaneously: - style_check: Code style and formatting - test_coverage: Test coverage analysis ### Phase 2: Security Review After initial checks pass, run security_scan (sequential to avoid conflicts). ### Phase 3: Synthesis Combine all findings into a unified review report with: - Critical issues (must fix) - Suggestions (should consider) - Positive observations (good practices found) extensions: - type: builtin name: developer - type: stdio name: github cmd: uvx args: ["github-mcp-server"] prompt: | Review PR #{{ pr_number }} with {{ review_depth }} depth. {% if review_depth == "quick" %} Focus only on critical issues and security concerns. {% elif review_depth == "thorough" %} Perform exhaustive analysis including performance review. {% endif %} Start by fetching the PR details, then orchestrate the review phases. ``` ### Best Practices for Complex Workflows 1. **Use sub-recipes for reusable components** - Define once, use across multiple recipes 2. **Parallelize independent tasks** - Multiple subagent calls in one message run concurrently 3. **Use `sequential_when_repeated: true`** - For tasks that shouldn't run in parallel (e.g., database migrations) 4. **Scope extensions appropriately** - Give subagents only the tools they need 5. **Use summary mode (default)** - Subagents return concise summaries; use `summary: false` only when you need full conversation history 6. **Handle failures gracefully** - Design workflows to continue even if one subagent fails ### Technical Details - Subagent tool: `crates/goose/src/agents/subagent_tool.rs` - Subagent execution: `crates/goose/src/agents/subagent_handler.rs` - Recipe sub_recipes field: `crates/goose/src/recipe/mod.rs` (SubRecipe struct) - Template rendering: `crates/goose/src/recipe/template_recipe.rs`