# Admin Setup Source: https://docs.ultra.security/admin-setup Get Ultra running in seconds [Request access](https://ultra.security/request-access) to Ultra. Once your request is approved, sign in to [Ultra Hub](https://hub.ultra.security) and download the installer for your platform from **Settings > Downloads**. Run the installer. It automatically detects your AI agents and configures them. **Quit and reopen** your AI agent (Claude Desktop, Cursor, Codex, etc.) so it picks up the Ultra connection. Ultra works with **Claude Desktop**, **Cursor**, **VS Code**, **GitHub Copilot**, **Claude Code**, **Codex**, **Windsurf**, **Roo Code**, **Goose**, **Gemini**, **Antigravity**, **LM Studio**, **Amazon Q Developer**, **Zed**, **Ona**, and **Hermes**. Ask your AI agent to check the connection: * *"Check your connection to Ultra"* Your agent should confirm that Ultra is installed and running. Ask your AI agent to add the MCP connectors you want to use: * *"Connect to the Notion MCP connector"* * *"Add the GitHub MCP connector"* * *"Set up the Linear MCP connector"* * *"Add the filesystem connector so you can read my project files"* Your agent will search the registry, install the connector, and handle any authentication setup. **Restart your AI agent** after adding connectors for them to become available. Make sure your agent installs the **official** MCP connector for each service, not a community fork. If unsure, ask your agent to confirm the publisher before installing. For centralized team management: 1. Go to [hub.ultra.security](https://hub.ultra.security) and create an account 2. Create a **workspace** for your team 3. Ask your AI agent to connect: * *"Log in to Ultra Hub"* * *"Link this device to Ultra Hub"* Once linked, your traces and audit events sync to Ultra Hub for team-wide visibility. From the Hub web UI: 1. Go to **Settings > Invites** 2. Click **Invite Member** 3. Enter the team member's email address and select a role 4. Click **Send Invite** The invited user receives an email with a link to join your organization. See [Roles & Permissions](/hub/rbac) for details on available roles. ```bash theme={null} curl -sSL https://get.ultra.security/install.sh | sh ``` This auto-detects your OS and architecture, downloads the latest release from CDN, and installs the `ultra` binary to `/usr/local/bin`. **macOS .pkg installer:** Download the signed and notarized `.pkg` from `get.ultra.security/v{VERSION}/Ultra-{VERSION}-macOS.pkg` and double-click to install. **Custom install directory:** ```bash theme={null} curl -sSL https://get.ultra.security/install.sh | INSTALL_DIR=~/.local/bin sh ``` See [Installation](/installation/install-ultra) for all options. Verify that Ultra is installed and your system is ready: ```bash theme={null} ultra doctor ``` This checks your OS, detects installed MCP agents, and validates dependencies like Node.js. Run the interactive install wizard to configure your MCP agent to use Ultra: ```bash theme={null} ultra install ``` ```bash theme={null} ultra install --agent claude ``` This configures Claude Desktop to route all MCP traffic through Ultra. ```bash theme={null} ultra install --agent cursor ``` ```bash theme={null} ultra install --agent codex ``` This configures Codex to route all MCP traffic through Ultra. ```bash theme={null} ultra install --yes ``` Installs into all detected agents without prompting. **Restart your MCP agent** after configuring for changes to take effect. Search the registry and add a connector: ```bash theme={null} ultra search filesystem ultra add @modelcontextprotocol/server-filesystem ``` Ultra searches the official MCP Registry and npm, then adds the connector to your configuration. You can also add remote connectors by URL: ```bash theme={null} ultra add https://mcp.example.com/sse ``` **Restart your MCP agent** after adding connectors. Start Ultra. Your MCP agent connects to Ultra, and Ultra proxies to your upstream connectors: ```bash theme={null} ultra start ``` Ultra is now recording every MCP operation. Restart your MCP agent to begin routing traffic through Ultra. For centralized team management: 1. Go to [hub.ultra.security](https://hub.ultra.security) and create an account 2. Create a **workspace** for your team 3. Authenticate and link your device: ```bash theme={null} ultra login ultra link ``` Once linked, your traces and audit events sync to Ultra Hub for team-wide visibility. From the Hub web UI: 1. Go to **Settings > Invites** 2. Click **Invite Member** 3. Enter the team member's email address and select a role 4. Click **Send Invite** The invited user receives an email with a link to join your organization. See [Roles & Permissions](/hub/rbac) for details on available roles. Open the local web dashboard to see your MCP traffic: ```bash theme={null} ultra dashboard ``` Navigate to `http://localhost:8080` to inspect traces, view audit logs, and monitor connected connectors locally. ## What's Next Understand the proxy architecture Customize your Ultra setup Deep dive into traces and audit logs Set up team management # ultra add Source: https://docs.ultra.security/cli/add Add an MCP connector from a registry or URL Add an MCP connector from the official MCP Registry, npm, or a remote URL. ## Usage ```bash theme={null} ultra add [flags] ``` ## Arguments | Argument | Required | Description | | - | - | - | | `connector` or `url` | Yes | Connector name/package or HTTP(S) URL | ## Flags | Flag | Short | Default | Description | | - | - | - | - | | `--name` | | Derived from package | Custom name for the connector | | `--disabled` | | `false` | Add connector in disabled state | | `--yes` | `-y` | `false` | Accept defaults without prompting | ## Examples ```bash theme={null} # Add the official filesystem connector ultra add @modelcontextprotocol/server-filesystem # Add by short name (searches registries) ultra add filesystem # Add with a custom name ultra add @modelcontextprotocol/server-filesystem --name myfiles # Add without prompts (use defaults) ultra add filesystem --yes # Add a remote connector by URL ultra add https://mcp.slack.com/mcp # Add a remote connector with a custom name ultra add https://remote.example.com/mcp --name myconnector ``` ## Registry Lookup When you pass a package name: * **Full name** (contains `/` or starts with `@`) — Direct lookup in registries * **Short name** — Searches registries and presents matching results for selection If multiple results are found, Ultra shows an interactive selector (up to 9 results). ## URL Mode When you pass an HTTP(S) URL: * Ultra derives a connector name from the hostname (e.g., `https://mcp.slack.com` → `slack`) * Tests the connection and detects authentication requirements * For OAuth connectors, authorization happens at runtime * For token-based auth, prompts for the token during setup ## Connection Testing For both registry and URL connectors, Ultra tests the connection: * **Stdio connectors** — Probes the connector with the MCP handshake. If the default args fail, automatically tries common fixes (e.g., appending `--stdio`) * **HTTP connectors** — Tests connectivity and detects auth requirements (OAuth, Bearer token, API key) ## Configuration Added connectors are written to Ultra's config file. Environment variables and auth tokens are collected during the interactive flow or can be set later: ```bash theme={null} ultra config set-token my-connector YOUR_TOKEN ``` # ultra config Source: https://docs.ultra.security/cli/config Manage Ultra configuration Set tokens, headers, and view configuration details. ## Usage ```bash theme={null} ultra config [flags] ``` ## Subcommands ### `set-token` Set an authentication token (PAT, API key) for an MCP connector. The token is stored as an `Authorization: Bearer` header. ```bash theme={null} ultra config set-token [token] ``` If the token is not provided as an argument, you'll be prompted to enter it securely (hidden input). | Flag | Short | Default | Description | | - | - | - | - | | `--interactive` | `-i` | `false` | Force interactive token input | ```bash theme={null} # Set token interactively (recommended) ultra config set-token github-mcp-server # Set token directly (less secure, appears in shell history) ultra config set-token github-mcp-server ghp_xxxxxxxxxxxx ``` ### `set-header` Set a custom HTTP header for an MCP connector. ```bash theme={null} ultra config set-header ``` ```bash theme={null} # Set a custom API key header ultra config set-header my-connector X-API-Key abc123 # Set an authorization header manually ultra config set-header my-connector Authorization "Bearer xyz" ``` ### `set` Set a top-level configuration value. ```bash theme={null} ultra config set [value] ``` | Key | Description | | - | - | | `deploy-key` | Store a workspace deploy key; clears any existing OAuth/sync tokens | | `device-email` | Store the device user's email for deploy-key identity resolution | | `drift-enforcement` | Control whether Ultra installs itself into agents you add later | If `value` is omitted for `deploy-key` or `device-email`, Ultra reads it from stdin (or the `ULTRA_DEPLOY_KEY` / `ULTRA_DEVICE_EMAIL` environment variable) so the secret never appears in shell history. ```bash theme={null} # Store a deploy key without putting it in shell history printf '%s\n' "$ULTRA_DEPLOY_KEY" | ultra config set deploy-key # Store the device user's work email ultra config set device-email user@example.com ``` #### Drift enforcement `drift-enforcement` controls whether Ultra keeps itself installed in the MCP agents it finds on this machine, and how far it goes: | Value | Behavior | | - | - | | `off` | Turn off drift monitoring entirely. Ultra neither reports nor corrects | | `detect_only` | Report drift without changing any agent's configuration | | `default` | Keep Ultra installed in the agents you have already set up. Other agents are reported but left alone | | `auto_install` | Also install Ultra into supported agents you add later, without changing those agents' other MCP connectors | | `enforce_all` | Everything `auto_install` does, and also moves each agent's remaining MCP connectors to run through Ultra | **`default` and `auto_install` differ in kind, not degree.** `default` brings new connectors under Ultra in the agents it already manages, but installs into nothing new. `auto_install` does the reverse: it installs Ultra into every agent it detects, and deliberately leaves those agents' other connectors exactly as they are. Neither is strictly stronger than the other, so pick the one matching what you want, rather than reading the list as a ladder. `enforce_all` is the only mode that does both. ```bash theme={null} # Opt into auto-installing Ultra into newly discovered agents ultra config set drift-enforcement auto_install # Return to the default ultra config set drift-enforcement default ``` `default` and `auto_install` are the two values the **auto-install** checkbox in the installer's agent-setup wizard toggles between. See [Configure Agents](/installation/configure-agents). `enforce_all` rewrites the configuration of MCP connectors you did not add through Ultra, so it is meant for centrally managed deployments rather than an individual machine. Set it deliberately. #### When your organization sets it for you An organization can set drift enforcement centrally, from **Governance** in Ultra Hub. When it has, **the value your organization set wins** and the local `ultra config set drift-enforcement` value is ignored — including when the organization's value is `default`. Two things follow that are worth knowing before you go looking for a bug: * **The change lands on the next start.** Your device resolves drift enforcement once when Ultra starts. A value that arrives afterwards is stored and takes effect the next time you run [`ultra start`](/cli/start), not immediately. * **`ultra config get drift-enforcement` reads your local value, not the effective one.** If your organization is setting it, the two can differ. While a device is unlinked from Hub, the local value is the one that applies. Linking to a workspace clears any previously stored organization value, so a device moved between workspaces never keeps the old one. See [Governance](/hub/governance#drift-enforcement) for setting this at the organization or workspace level. ### `get` Read back a configuration value. ```bash theme={null} ultra config get ``` | Key | Description | | - | - | | `drift-enforcement` | Print the agent drift-detection enforcement mode | ```bash theme={null} ultra config get drift-enforcement ``` ### `path` Show the configuration file path. ```bash theme={null} ultra config path ``` ## Examples ```bash theme={null} # Set a token for a connector ultra config set-token github-mcp-server YOUR_TOKEN # Set a custom header ultra config set-header linear "X-API-Key" YOUR_API_KEY # Opt into auto-installing Ultra into newly discovered agents ultra config set drift-enforcement auto_install # Check the current drift-detection enforcement mode ultra config get drift-enforcement # Show current config path ultra config path ``` # ultra connectors Source: https://docs.ultra.security/cli/connectors Manage configured MCP connectors List, add, remove, enable, and disable upstream MCP connectors. `ultra servers` remains available as an alias for `ultra connectors`. ## Usage ```bash theme={null} ultra connectors [subcommand] [flags] ``` Running `ultra connectors` without a subcommand lists all configured connectors. ## Subcommands ### `list` (default) List all configured connectors with status, transport info, and description. ```bash theme={null} ultra connectors ultra connectors list ultra connectors ls ``` ### `add` Add a new MCP connector manually. ```bash theme={null} ultra connectors add [flags] ``` | Flag | Short | Default | Description | | - | - | - | - | | `--command` | | | Command to execute (stdio transport) | | `--args` | | | Command arguments (space-separated) | | `--url` | | | Connector URL (HTTP transport) | | `--description` | `-d` | | Connector description | | `--env` | `-e` | | Environment variables (`KEY=VALUE`, repeatable) | | `--disabled` | | `false` | Add in disabled state | ```bash theme={null} # Stdio connector ultra connectors add filesystem \ --command npx \ --args "-y @modelcontextprotocol/server-filesystem /tmp" # HTTP connector ultra connectors add remote-api --url https://mcp.example.com/sse # With environment variables ultra connectors add github \ --command npx \ --args "-y @modelcontextprotocol/server-github" \ --env "GITHUB_TOKEN=ghp_xxx" # Disabled initially ultra connectors add test-connector --command ./test-server --disabled ``` ### `remove` Remove a connector from configuration. ```bash theme={null} ultra connectors remove [flags] ``` | Flag | Short | Default | Description | | - | - | - | - | | `--force` | `-f` | `false` | Skip confirmation prompt | Aliases: `rm`, `delete` ### `enable` Enable a disabled connector. ```bash theme={null} ultra connectors enable ``` ### `disable` Disable a connector without removing it. ```bash theme={null} ultra connectors disable ``` To re-authenticate an OAuth connector whose sign-in has expired, use the top-level [`ultra reconnect`](/cli/reconnect) command. ## Examples ```bash theme={null} # List all configured connectors ultra connectors # Add a connector manually ultra connectors add filesystem --command npx --args "-y @modelcontextprotocol/server-filesystem /tmp" # Remove a connector ultra connectors remove filesystem # Enable/disable a connector ultra connectors enable filesystem ultra connectors disable filesystem ``` For adding connectors from registries, use [`ultra add`](/cli/add) instead — it handles registry lookup, configuration prompts, and connection testing automatically. # ultra dashboard Source: https://docs.ultra.security/cli/dashboard Start the web monitoring dashboard Start a web-based dashboard for monitoring Ultra's observability data. ## Usage ```bash theme={null} ultra dashboard [flags] ``` ## Flags | Flag | Short | Default | Description | | - | - | - | - | | `--address` | `-a` | `:8080` | Listen address for the dashboard | | `--dev` | | `false` | Enable development tools | ## Examples ```bash theme={null} # Start dashboard on default port ultra dashboard # Start dashboard on custom port ultra dashboard --address :9090 ``` ## Features The dashboard provides: * Real-time view of MCP traffic * Trace inspection and debugging * Audit log viewer * Connected connector status ## Concurrent Usage You can run the dashboard while Ultra is running to monitor traffic in real time. ```bash theme={null} # Terminal 1: Run the proxy ultra start # Terminal 2: View traffic ultra dashboard ``` # ultra doctor Source: https://docs.ultra.security/cli/doctor Run system diagnostics and upload reports to Hub Run diagnostic checks to verify Ultra is properly configured. Optionally upload a sanitized diagnostics report to Ultra Hub for fleet debugging. ## Usage ```bash theme={null} ultra doctor [flags] ``` ## Flags | Flag | Short | Default | Description | | - | - | - | - | | `--verbose` | `-v` | `false` | Show detailed output | | `--debug` | | `false` | Show detailed detection diagnostics | | `--report` | | `false` | Upload a sanitized diagnostics report to Ultra Hub | | `--dry-run` | | `false` | With `--report`, print the payload to stdout instead of uploading | ## Examples ```bash theme={null} # Run diagnostics ultra doctor # Show detailed output ultra doctor --verbose # Upload a diagnostics report to Hub ultra doctor --report # Preview the report payload without uploading ultra doctor --report --dry-run ``` ## Checks performed Checks are grouped into four sections: **System**, **Agents**, **Configuration**, and **Dependencies**. ### System | Check | What it reports | | - | - | | **Go Runtime** | Go version and platform | | **Operating System** | OS compatibility (macOS, Linux, Windows) | ### Agents Doctor lists a row only for the agents it detects on this machine, either because the agent is installed or because its config was found. Supported agents that aren't detected are left out. See [Supported Agents](/installation/configure-agents#supported-agents) for the full list, including Claude Desktop, Cursor, VS Code, Claude Code, and Codex. Each agent row shows one of these results: | Result | Status | Meaning | | - | - | - | | `3 connectors`, `1 connector`, or `no connectors` | Pass | Ultra read the agent's MCP config. The count includes every MCP connector in it, Ultra included | | `no MCP config` | Warning | The agent is installed but has no MCP config yet. Run `ultra install --agent ` to configure it | | `config detected, binary not in PATH` | Warning | Ultra found the agent's config directory but couldn't locate the agent itself | | `config parse failed` | Warning | Ultra couldn't parse the agent's config file. The file path and the parser's error appear under the row, even without `--verbose`. Fix the file, then run `ultra doctor` again | The **Agents** section always ends with a **Config Drift** check, which looks for MCP connectors that aren't running through Ultra: | Result | Status | Meaning | | - | - | - | | `2 agent(s) fully managed` | Pass | At least one agent is fully managed, and no detected agent has connectors outside Ultra | | `1 agent(s) with unmanaged connectors` | Warning | At least one agent has connectors outside Ultra. With `--verbose`, and under **Recommendations**, doctor lists each agent and its unmanaged connectors | | `no agents migrated yet` | Warning | No detected agent is fully managed yet. Run `ultra install`, then [`ultra migrate`](/cli/migrate) | ### Configuration | Check | What it reports | | - | - | | **Config Directory** | Ultra config directory exists (`~/.config/ultra/`, or `%APPDATA%\ultra` on Windows) | | **Config File** | Ultra configuration file exists | ### Dependencies | Check | What it reports | | - | - | | **Node.js** | Required for npm-based MCP connectors. A warning if it isn't found | | **Docker** | Optional, for containerized MCP connectors. A missing Docker passes as `not installed (optional)`. Doctor warns only when Docker is installed but `docker --version` fails (`unavailable`) or doesn't respond (`timed out`) | ## Output Each check shows a status icon: * **Pass**: Check succeeded, or an optional component such as Docker isn't installed * **Warning**: Something needs attention, such as an agent config Ultra couldn't parse or a missing Node.js * **Error**: Required component missing or broken The summary shows total checks, passes, warnings, and errors, with recommendations for any issues found. ## Diagnostics reporting When you pass `--report`, Ultra collects a sanitized snapshot of your device's configuration, detected MCP agents, and recent log artifacts, then uploads the bundle to Ultra Hub. This helps your organization's admins and Ultra support diagnose installation or connectivity issues across a fleet of machines. ### What is collected | Section | Contents | | - | - | | **Config snapshot** | Your `ultra.yaml` with tokens, secrets, and environment variables masked as `[REDACTED]` | | **Detected agents** | Which MCP agents are installed, whether Ultra is configured in each, and any detection errors | | **Hub state** | Whether the device is linked, the workspace ID, sync interval, and offline mode setting | | **Log artifacts** | The last 16 KB of `sync.log`, `crash.log`, post-install log, and setup agent log (if present) | | **Managed preferences** | Whether MDM-managed keys (`AutoSetup`, `DeployKey`, etc.) are present on macOS | | **System info** | Ultra version, OS, and architecture | ### Privacy and redaction The diagnostics payload is automatically redacted before it leaves your machine: * API keys, tokens, and secrets are replaced with `[REDACTED]` * Your home directory path is replaced with `~` * Hostnames are replaced with `[REDACTED_HOSTNAME]` * Credential patterns (Bearer tokens, JWTs, AWS keys, GitHub tokens, PEM private keys, database URLs) are detected and scrubbed Use `--report --dry-run` to inspect the exact payload before uploading. ### Requirements * The device must be authenticated with Ultra Hub (`ultra login`) * The report is uploaded to `POST /api/v1/diagnostics` and stored in S3 for Ultra employees only * A `gateway.diagnostics_reported` security event appears in your organization's [Admin Log](/hub/admin-log) when `--report` is used manually Automatic background diagnostic uploads (during install auto-link, for example) do not create Admin Log entries. # ultra install Source: https://docs.ultra.security/cli/install Install Ultra into MCP agents Detect installed MCP agents and configure them to use Ultra as their MCP server. ## Usage ```bash theme={null} ultra install [flags] ``` ## Flags | Flag | Short | Default | Description | | - | - | - | - | | `--agent` | | | Target agent (`claude`, `cursor`, `vscode`, `cline`, `claude_code`, `windsurf`, `goose`, `codex`, `grok`, `opencode`, `gemini`, `antigravity`, `roocode`, `amazonq`, `lmstudio`, `zed`, `ona`, `hermes`) | | `--all` | | `false` | Install into all detected MCP agents (non-interactive) | | `--config` | | | Path to Ultra config file | | `--no-backup` | | `false` | Skip backup of existing agent config | | `--yes` | `-y` | `false` | Skip confirmation prompt | | `--list` | `-l` | `false` | List detected agents without installing | `--agent` replaces the former `--client` flag. `--client` and its `-c` shorthand remain as deprecated aliases and print a pointer to `--agent` when used. ## Examples ```bash theme={null} # Interactive installation wizard ultra install # Install into Claude Desktop only ultra install --agent claude # Install into Cursor only ultra install --agent cursor # Install into Codex only ultra install --agent codex # Install into Grok Build only ultra install --agent grok # Install into Antigravity ultra install --agent antigravity # Install into Zed ultra install --agent zed # Install into Ona ultra install --agent ona # Install into Hermes ultra install --agent hermes # List detected agents without installing ultra install --list # Install into all detected agents (non-interactive, used by MDM) ultra install --all ``` ## Supported Agents | Agent | Flag Value | Aliases | | - | - | - | | Claude Desktop | `claude` | `claude-desktop`, `claude_desktop` | | Cursor | `cursor` | | | Codex | `codex` | `codex-cli` | | Grok Build | `grok` | `grok-build`, `grok_build` | | VS Code | `vscode` | | | Claude Code | `claude_code` | `claude-code` | | Windsurf | `windsurf` | | | Goose | `goose` | | | OpenCode | `opencode` | | | Gemini | `gemini` | | | Antigravity | `antigravity` | `agy` | | Roo Code | `roocode` | `roo-code`, `roo_code` | | Amazon Q | `amazonq` | `amazon-q`, `amazon_q` | | LM Studio | `lmstudio` | `lm-studio`, `lm_studio` | | Zed | `zed` | | | Ona | `ona` | | | Hermes | `hermes` | | ## What It Does The install command writes Ultra as an MCP server entry in the agent's configuration. For example, Claude Desktop's config becomes: ```json theme={null} { "mcpServers": { "ultra": { "command": "/usr/local/bin/ultra", "args": ["start"] } } } ``` For Codex, the TOML config at `~/.codex/config.toml` becomes: ```toml theme={null} [mcp_servers.ultra] command = "/usr/local/bin/ultra" args = ["start"] ``` For Hermes, the YAML config at `~/.hermes/config.yaml` becomes: ```yaml theme={null} mcp_servers: ultra: command: /usr/local/bin/ultra args: - start enabled: true ``` Existing connectors and any non-MCP top-level keys in the Hermes config are preserved. Connectors with `enabled: false` are filtered out when discovering existing entries. If `--config` is specified, the args include `--config `. ## Auto-linking with deploy keys Ultra auto-links the device to Hub at install time when invoked via: * **`ultra install --all`** — used by MDM deployments to configure every detected agent in one shot * **`ultra install --agent ona`** — used by the Ona devcontainer entry point, which needs the device named before any MCP agent starts After configuring agents, these paths check for a [deploy key](/hub/deploy-keys) (via `ULTRA_DEPLOY_KEY` environment variable, config file, or managed preferences). If a deploy key is found, the device is linked immediately: * The device registers with the workspace scoped by the deploy key * Device identity (email, external ID) is resolved from available sources (MDM preferences, environment variables) * The device appears in the Hub dashboard before any MCP agent launches For all other install paths — the interactive wizard, plain `ultra install --yes`, and per-agent installs other than Ona — the device links during the first `ultra start` instead. OAuth-authenticated users always link at `ultra start`, regardless of which install path was used. ## Backups By default, the existing agent config is backed up before modification. Use `--no-backup` to skip. # ultra link Source: https://docs.ultra.security/cli/link Link this device to a workspace Link this Ultra device to a workspace in Ultra Hub. After linking, the device syncs traces and audit events to Hub. ## Usage ```bash theme={null} ultra link [flags] ``` ## Flags | Flag | Short | Default | Description | | - | - | - | - | | `--workspace` | `-w` | | Workspace ID to link to | | `--name` | `-n` | hostname | Name for this device | ## Examples ```bash theme={null} # Link interactively (choose workspace) ultra link # Link to a specific workspace ultra link --workspace ws_xxxxxxxxxxxx # Link with a custom device name ultra link --name "My MacBook" ``` ## Prerequisites You must be logged in first: ```bash theme={null} ultra login ``` ## Interactive Flow 1. Fetches your available workspaces from Hub 2. Shows a selector to choose a workspace 3. Prompts for a device name (defaults to hostname) 4. Registers the device with Hub 5. Saves device ID and workspace ID to config ## After Linking ```bash theme={null} ultra start # Traces and events will sync to Hub automatically ``` ## Already Linked If the device is already linked, Ultra shows the current device ID and workspace. Use `ultra unlink` to disconnect before re-linking. # ultra login Source: https://docs.ultra.security/cli/login Authenticate with Ultra Hub Authenticate with Ultra Hub to enable cloud features like device linking, telemetry sync, and team management. ## Usage ```bash theme={null} ultra login [flags] ``` ## Flags | Flag | Default | Description | | - | - | - | | `--hub` | `https://hub.ultra.security` | Ultra Hub URL | ## Examples ```bash theme={null} # Login to Ultra Hub ultra login # Login to a self-hosted hub ultra login --hub https://hub.mycompany.com ``` ## How It Works 1. Ultra starts a local callback server on port 19284 2. Your default browser opens to the Hub authentication page 3. You sign in or create an account 4. Hub redirects back to the local server with credentials 5. Credentials are saved to your config file If the browser can't be opened automatically, Ultra displays the URL for manual navigation. The login flow times out after 5 minutes. ## After Login Your credentials are stored in `~/.config/ultra/config.yaml`. Next step: ```bash theme={null} ultra link # Link this device to a workspace ``` ## Session Management * If already logged in, Ultra tells you and exits * If your session has expired, Ultra proceeds with re-authentication * Credentials persist across Ultra restarts * Use `ultra logout` to clear credentials # ultra logout Source: https://docs.ultra.security/cli/logout Log out from Ultra Hub Log out from Ultra Hub and clear stored credentials. If the device is currently linked, it will be unlinked first. ## Usage ```bash theme={null} ultra logout [flags] ``` ## Flags | Flag | Short | Default | Description | | - | - | - | - | | `--force` | `-f` | `false` | Skip confirmation prompt | ## Examples ```bash theme={null} # Logout (with confirmation) ultra logout # Logout without confirmation ultra logout --force ``` ## What Happens 1. If linked, unlinks the device from Hub (best effort) 2. Clears all Hub credentials (access token, refresh token, sync token) 3. Disables Hub connectivity 4. Saves updated config ## After Logout ```bash theme={null} ultra login # Sign in again ``` # ultra migrate Source: https://docs.ultra.security/cli/migrate Import existing MCP connectors into Ultra Discover MCP connectors configured in your agents and import them into Ultra's configuration. ## Usage ```bash theme={null} ultra migrate [flags] ``` ## Flags | Flag | Short | Default | Description | | - | - | - | - | | `--config` | | | Path to Ultra config file | | `--yes` | `-y` | `false` | Skip confirmation prompt | | `--list` | `-l` | `false` | List discovered connectors without migrating | | `--dry-run` | | `false` | Preview changes without applying | | `--from` | | | Migrate from specific agent only (`claude`, `cursor`, `codex`, `ona`, `hermes`, etc.) | | `--connector` | | | Migrate only the named connector(s); repeatable (formerly `--server`, which remains a deprecated alias) | ## Examples ```bash theme={null} # Interactive migration wizard ultra migrate # List discovered MCP connectors without migrating ultra migrate --list # Dry run to preview changes ultra migrate --dry-run # Migrate from a specific agent only ultra migrate --from claude # Non-interactive migration ultra migrate --yes # Migrate into a named profile's config ultra --profile dev migrate ``` ## How It Works 1. Scans Claude Desktop, Cursor, Codex, Ona, Hermes, and other agent config files for MCP connector entries 2. Deduplicates connectors found in multiple agents 3. Validates connector configurations and warns about issues 4. Writes discovered connectors to Ultra's config as upstream entries 5. Updates agent configs to point to Ultra ## Dry Run The `--dry-run` flag shows exactly what Ultra config would be generated without making any changes. Useful for reviewing the migration before committing. ## Profiles `ultra migrate` respects the global `--profile` flag. When a profile is set, the migration writes to that profile's config path instead of the default. ```bash theme={null} # Default: writes to ~/.config/ultra/config.yaml ultra migrate # With profile: writes to ~/.config/ultra/profiles/dev/config.yaml ultra --profile dev migrate ``` You can also set the `ULTRA_PROFILE` environment variable instead of passing `--profile` on every invocation. # CLI Overview Source: https://docs.ultra.security/cli/overview Ultra command reference Ultra's CLI is organized into categories. Every command supports `--help` for detailed usage information. ```bash theme={null} ultra --help ultra --help ``` ## Command Categories ### Core | Command | Description | | - | - | | [`ultra start`](/cli/start) | Start Ultra as an MCP proxy server | | [`ultra dashboard`](/cli/dashboard) | Start the web monitoring dashboard | ### Setup | Command | Description | | - | - | | [`ultra install`](/cli/install) | Install Ultra into MCP agents | | [`ultra migrate`](/cli/migrate) | Import existing MCP connectors into Ultra | ### Management | Command | Description | | - | - | | [`ultra search`](/cli/search) | Search MCP connector registries | | [`ultra add`](/cli/add) | Add an MCP connector from registry or URL | | [`ultra connectors`](/cli/connectors) | List, add, remove, enable, disable connectors | | [`ultra reconnect`](/cli/reconnect) | Re-authenticate OAuth MCP connectors | | [`ultra config`](/cli/config) | Manage configuration (tokens, headers, path) | ### Hub | Command | Description | | - | - | | [`ultra login`](/cli/login) | Authenticate with Ultra Hub | | [`ultra link`](/cli/link) | Link this device to a workspace | | [`ultra unlink`](/cli/unlink) | Unlink this device from its workspace | | [`ultra uninstall`](/cli/uninstall) | Remove Ultra for the current user | | [`ultra logout`](/cli/logout) | Log out from Ultra Hub | ### Utility | Command | Description | | - | - | | [`ultra doctor`](/cli/doctor) | Run system diagnostics | | `ultra update` | Check for and install Ultra updates and releases | | [`ultra version`](/cli/version) | Show version information | ## Global Behavior * All commands use styled terminal output via the Charm stack * Commands support both interactive (wizard) and non-interactive (flags) modes * Configuration is auto-discovered from standard locations (see [Configuration](/configuration/config-file)) * Exit code `0` for success, `1` for errors # ultra reconnect Source: https://docs.ultra.security/cli/reconnect Re-authenticate OAuth MCP connectors from the terminal Re-authenticate configured OAuth MCP connectors from the terminal. This is the CLI equivalent of the [`ultra_reconnect`](/getting-started/ultra-mcp-tools#ultra_reconnect) tool. Use it when you're working in a terminal or your agent can't surface the in-app [Reconnect](/getting-started/mcp-app#reconnecting-a-connector) link. ## Usage ```bash theme={null} # Re-authenticate one connector ultra reconnect # Re-authenticate every connector that needs it ultra reconnect ``` The `` must be an enabled HTTP upstream that authenticates with OAuth. With no connector name, Ultra reconnects every enabled OAuth connector whose sign-in is missing or expired. See [Reconnecting every connector](#reconnecting-every-connector). ## Flags | Flag | Short | Default | Description | | - | - | - | - | | `--config` | `-c` | | Path to Ultra config file | ## How It Works 1. Resolves `` to a configured, enabled HTTP upstream 2. Opens your browser to sign in, and always prints the authorize URL as a headless fallback 3. Waits for you to finish signing in (press `Ctrl+C` to cancel) 4. Saves the new token to Ultra's shared credential store Because the CLI can't refresh an agent that is already running, **restart your agent** (or `ultra start`) afterward so its Ultra device process loads the new token. ## Reconnecting every connector Run `ultra reconnect` with no connector name to re-authenticate everything that needs it in one pass. Ultra picks the connectors to reconnect for you and skips the ones it can't help: | Connector state | Included | | - | - | | Sign-in missing or expired and not refreshable | Yes | | Already signed in, or expired but refreshable on its own | No | | Disabled, or using stdio rather than an HTTP URL | No | | Authenticating with a static token or API key instead of OAuth | No | If nothing needs attention, Ultra prints `Nothing to reconnect.` and exits. Connectors are handled one at a time, each with its own browser sign-in. While a batch is running: * Press `Ctrl+C` once to skip the current connector and move to the next one * Press `Ctrl+C` twice in quick succession to stop the batch, leaving the remaining connectors unattempted A failed connector doesn't stop the run. Ultra prints the error, continues, and shows a summary at the end listing what was reconnected, skipped, failed, or never attempted. The command exits non-zero if any connector was skipped, failed, or left unattempted. ## Examples ```bash theme={null} # Re-authenticate an OAuth connector after its sign-in expired ultra reconnect notion # Re-authenticate every connector whose sign-in has lapsed ultra reconnect # Use a config file outside the default location ultra reconnect notion --config ./ultra.yaml ``` `ultra reconnect` only applies to OAuth connectors. If the connector authenticates with a token or API key instead, update its credential with `ultra config set-token ` (see [Authentication](/configuration/upstream-connectors#authentication)). Ultra returns a clear error if the connector is unknown, disabled, uses stdio transport, or doesn't use browser-based OAuth. # ultra search Source: https://docs.ultra.security/cli/search Search for MCP connectors in registries Search the official MCP Registry and npm for available MCP connectors. ## Usage ```bash theme={null} ultra search [flags] ``` ## Arguments | Argument | Required | Description | | - | - | - | | `query` | Yes | Search term | ## Flags | Flag | Short | Default | Description | | - | - | - | - | | `--limit` | `-n` | `10` | Maximum number of results | | `--official` | | `false` | Only show official connectors | | `--source` | | | Only search specific source (`official`, `npm`) | ## Examples ```bash theme={null} # Search for filesystem connectors ultra search filesystem # Search for database connectors ultra search database # Search only official connectors ultra search github --official # Limit results ultra search api --limit 5 ``` ## Sources Ultra searches two registries: 1. **Official MCP Registry** — Curated connectors from `registry.modelcontextprotocol.io` 2. **npm** — Community MCP connector packages from npmjs.com Results show connector name, source, description, version, author, weekly downloads, and last updated date. Official and verified connectors are marked with badges. ## Next Step Use `ultra add ` to install a connector from the results. # ultra start Source: https://docs.ultra.security/cli/start Start Ultra as an MCP proxy server Start Ultra as an MCP server that proxies requests to upstream MCP connectors. Ultra acts as an intermediary between MCP agents and upstream connectors, providing observability, audit logging, and metrics. ## Usage ```bash theme={null} ultra start [flags] ``` ## Flags | Flag | Short | Default | Description | | - | - | - | - | | `--config` | `-c` | Auto-discovered | Path to configuration file | ## Examples ```bash theme={null} # Start with default config ultra start # Start with custom config ultra start --config ~/.config/ultra/config.yaml ``` ## What Happens 1. Loads configuration (auto-discovers or uses `--config` path) 2. Prepares to record traffic and audit events 3. Sets up the observability pipeline 4. Connects to all enabled upstream connectors 5. Registers aggregated tools, resources, and prompts 6. Starts the MCP server on stdio transport 7. If Hub is configured, starts background sync Ultra communicates with the MCP client over stdio (stdin/stdout). All log output goes to stderr to avoid interfering with the MCP protocol. ## Signals * **Ctrl+C** (SIGINT) or **SIGTERM** — Graceful shutdown. Ultra stops upstream connections, flushes pending data, and exits. * Client disconnect (EOF/pipe close) — Ultra exits gracefully when the MCP client disconnects. ## Hub Sync When Hub is enabled and the device is linked, `ultra start` runs background sync: * **Data sync** at the configured interval (default: 60s) * **Heartbeats** at half the sync interval (minimum 30s) * OAuth token refresh happens automatically * Terminal auth errors disable sync with a single warning (no error spam) # ultra uninstall Source: https://docs.ultra.security/cli/uninstall Remove Ultra for the current user Remove Ultra's agent configuration, credentials, state, and managed runtime for the current user. This is scoped to the user who runs it. It does not remove Ultra for other users on the machine. ## Usage ```bash theme={null} ultra uninstall [flags] ``` ## Flags | Flag | Short | Default | Description | | - | - | - | - | | `--yes` | `-y` | `false` | Skip confirmation prompt | | `--restore-connectors` | | `false` | Restore missing direct connectors from existing backups | ## Examples ```bash theme={null} # Uninstall interactively (with confirmation) ultra uninstall # Uninstall without confirmation ultra uninstall --yes # Put direct connectors back into your agent config before removing Ultra ultra uninstall --restore-connectors ``` ## What Happens 1. Stops any running Ultra process 2. Removes Ultra from each agent configuration it manages 3. Optionally restores direct connectors, if `--restore-connectors` is set 4. Removes user data, including credentials and local state 5. Reports each step as it goes, naming the agent and config path it touched ## Direct Connectors When Ultra is installed it takes over your agent's connector configuration, keeping a timestamped backup first. Uninstalling does **not** put those direct connectors back by default. Your agents will simply have no connectors configured. Pass `--restore-connectors` to merge the missing ones from the newest readable backup before Ultra is removed. Existing backups are retained either way, so you can restore them by hand later. **Windows.** Uninstalling the Ultra MSI from Apps settings runs this cleanup and then removes the executable and the PATH entry. Running `ultra uninstall` directly performs the cleanup but leaves the MSI-managed binary in place. ## Reinstalling See [Install Ultra](/installation/install-ultra) to set Ultra up again. A fresh install starts from a clean configuration, so you will need to add your connectors and link the device again. # ultra unlink Source: https://docs.ultra.security/cli/unlink Unlink this device from its workspace Unlink this Ultra device from its workspace. After unlinking, the device stops syncing with Ultra Hub. Local data and configuration are preserved. ## Usage ```bash theme={null} ultra unlink [flags] ``` ## Flags | Flag | Short | Default | Description | | - | - | - | - | | `--force` | `-f` | `false` | Skip confirmation prompt | ## Examples ```bash theme={null} # Unlink interactively (with confirmation) ultra unlink # Unlink without confirmation ultra unlink --force ``` ## What Happens 1. Notifies Hub that this device is disconnecting 2. Clears device ID and workspace ID from local config 3. Preserves authentication tokens (for future re-linking) 4. Keeps all local data intact If the Hub notification fails (e.g., network error), the local cleanup still proceeds. ## Re-Linking After unlinking, you can link to the same or a different workspace: ```bash theme={null} ultra link ``` # ultra version Source: https://docs.ultra.security/cli/version Show version information Display Ultra version, build information, and system details. ## Usage ```bash theme={null} ultra version [flags] ``` ## Flags | Flag | Short | Default | Description | | - | - | - | - | | `--short` | `-s` | `false` | Print only the version number | ## Examples ```bash theme={null} # Full version info ultra version # Version number only (useful in scripts) ultra version --short ``` ## Output Full output includes: * **Version** — Release version * **Commit** — Git commit hash (truncated to 7 chars) * **Built** — Build date * **Go** — Go runtime version * **OS/Arch** — Operating system and architecture # Aggregator Source: https://docs.ultra.security/concepts/aggregator Multi-connector routing and tool namespacing The aggregator is Ultra's central orchestrator. It manages connections to multiple upstream MCP connectors and presents their combined tools, resources, and prompts as a single unified interface to the MCP agent. ## How It Works Your agent sees Ultra as one server. Behind the scenes, Ultra connects to multiple upstream connectors and merges their capabilities: ```mermaid theme={null} graph LR subgraph Tools["Agent view"] direction TB T1["filesystem__ls"] T2["filesystem__read"] T3["github__search"] T4["github__create_pr"] T5["notion__query"] T6["ultra_status"] end FS["Filesystem Connector"] GH["GitHub Connector"] NT["Notion Connector"] UB["Ultra (built-in)"] T1 --> FS T2 --> FS T3 --> GH T4 --> GH T5 --> NT T6 --> UB ``` ## Tool Namespacing To avoid name conflicts when multiple connectors provide similarly-named tools, the aggregator prefixes tool names with the upstream connector name: ``` {upstream_name}__{tool_name} ``` For example, if you have a `filesystem` connector with a `read` tool and a `github` connector with a `read` tool, they become: * `filesystem__read` * `github__read` This ensures every tool has a unique name that the agent can unambiguously reference. ## Routing When a tool call comes in, the aggregator: 1. Parses the namespaced tool name to identify the upstream connector 2. Routes the request through the [pipeline](/concepts/pipeline) for observability 3. Forwards the call to the correct upstream connector 4. Returns the response back through the pipeline to the agent The same routing applies to resources and prompts. ## Connection Management The aggregator handles the lifecycle of upstream connections: ### Stdio Connectors For command-based connectors, the aggregator: * Spawns the process with configured command, args, and env vars * Communicates over stdin/stdout * Handles process lifecycle (start, restart, cleanup) ### HTTP Connectors For URL-based connectors, the aggregator: * Establishes HTTP/SSE connections, automatically detecting the correct transport type (SSE vs. Streamable HTTP) * Manages connection health * Handles authentication (including OAuth2 flows) ## Connector Probing and MCP Normalization MCP connectors are notoriously inconsistent. Some require `--stdio` flags, others need subcommands like `server` or `serve`, and many output help text instead of speaking the MCP protocol by default. Ultra handles all of this automatically so you don't need a separate debugger tool. ### Stdio Connector Probing When Ultra connects to a stdio connector, it performs an MCP protocol handshake to verify the connector responds with valid JSON-RPC. If the handshake fails because the connector outputs non-JSON (help text, usage info, or error messages), Ultra automatically tries common argument patterns concurrently: * `--stdio` * `server` * `serve` * `server --stdio` * `server --mcp-only` * `start --stdio` * `--mcp` If any combination works, Ultra uses it automatically and stores the corrected args in your config. You never need to figure out the right invocation yourself. This probing happens during [`ultra add`](/cli/add) so connectors are validated before they're added to your configuration. ### HTTP Connector Probing For HTTP-based connectors, Ultra probes the connection and detects authentication requirements: | Connector Response | What Ultra Does | | - | - | | 200 / 204 | Connects directly, no auth needed | | 401 | Checks for OAuth support (RFC 8414) and initiates the appropriate auth flow | | 403 | Prompts for a bearer token or API key | Ultra also auto-detects the correct HTTP transport type from the connector URL, choosing SSE or Streamable HTTP automatically. ### Auto-Reconnection Connections drop. Connectors restart. Networks hiccup. Ultra detects stale connections (EOF, broken pipe, connection reset) and transparently reconnects without interrupting your workflow. Each upstream connector reconnects independently, so one flaky connector doesn't block the others. ### Actionable Error Messages When a connector can't be connected or auto-fixed, Ultra provides context-specific guidance rather than a generic error: * **Auth errors**: Tells you whether the connector needs OAuth or a token, with setup instructions * **Protocol errors**: Suggests the connector may need a specific flag, with a link to your config file * **Network errors**: Distinguishes between "connector unreachable" and "connector timed out" with appropriate next steps ## OAuth2 Support The aggregator supports OAuth2 authentication for upstream connectors that require it. When a connector responds with a 401, Ultra can: * Initiate an OAuth2 PKCE flow * Open a browser for user authorization * Store and refresh tokens automatically ## Built-in Tools Ultra registers one built-in tool: | Tool | Description | | - | - | | `ultra_status` | Returns Ultra's health status, version, upstream count, and aggregated tool count | This tool is always available regardless of upstream configuration. ## Startup Flow When `ultra start` runs: 1. Load configuration and validate upstream entries 2. Initialize storage and observability 3. Create the pipeline with interceptors 4. **Start the aggregator** — connect to all enabled upstream connectors 5. **Discover capabilities** — list tools, resources, and prompts from each upstream 6. **Register aggregated capabilities** — namespaced tools/resources/prompts on the MCP server 7. Start the MCP server on stdio transport 8. (Optional) Start Hub background sync # Architecture Source: https://docs.ultra.security/concepts/architecture Ultra's system architecture Ultra is built as a layered proxy with clear separation between transport, processing, routing, and storage. It runs locally on each user's machine as a single binary -- MCP agents launch it as a subprocess, and all processing happens on-device. There are no cloud components in the data path. ## System Architecture ```mermaid theme={null} graph TD Clients["MCP Agents"] subgraph UltraBox[" "] direction TB UltraLabel["Ultra"] Transport["Transport"] subgraph PipelineBox["Pipeline"] direction LR Trace["Trace"] Logging["Logging"] Audit["Audit"] Metrics["Metrics"] Trace --> Logging --> Audit --> Metrics end Aggregator["Aggregator"] UltraLabel ~~~ Transport Transport --> PipelineBox PipelineBox --> Aggregator end Server1["Upstream Connector"] Server2["Upstream Connector"] ServerN["Upstream Connector"] Clients -->|"MCP"| Transport Aggregator --> Server1 Aggregator --> Server2 Aggregator --> ServerN style UltraLabel fill:transparent,stroke:transparent ``` ## Layers ### Transport Layer Handles MCP protocol communication. Ultra supports all MCP transport types: * **stdio** — Standard I/O for local agents (Claude Desktop, Cursor, Codex) * **HTTP/SSE** — Server-sent events over HTTP * **Streamable HTTP** — Modern HTTP streaming The agent-facing side typically uses stdio (since MCP agents launch Ultra as a subprocess), while upstream connections use whatever transport the connector requires. ### Pipeline The [interceptor chain](/concepts/pipeline) processes every request and response, handling tracing, logging, auditing, and metrics. ### Aggregator The [aggregator](/concepts/aggregator) manages connections to upstream MCP connectors and presents them as a single unified interface to the agent. ### Storage Observability data is stored locally on the device by default, with optional sync to Ultra Hub for cloud deployments. # Ultra Ledger Source: https://docs.ultra.security/concepts/ledger A continuously-updated, cryptographically-verifiable security registry for the MCP ecosystem Ultra Ledger is a security registry for Model Context Protocol (MCP) servers. It catalogs servers from across the fragmented MCP registry landscape, scans them for vulnerabilities and behavioral changes, publishes Sigstore-signed trust signals, and feeds those signals into Ultra's runtime security decisions. The Ledger lives at [ledger.ultra.security](https://ledger.ultra.security) and is free to use without an Ultra account. ## Why it exists The MCP ecosystem grew fast and fragmented. The official MCP Registry hosts metadata for thousands of servers but explicitly delegates security scanning to subregistries. Ten-plus parallel registries and directories (Smithery, Glama, mcp.so, PulseMCP, Cursor Directory, and more) each carry inconsistent quality signals, and none provide deep security analysis. Recent research found that 41% of the 518 servers in the official MCP Registry ship with no authentication at all ([Kai Security AI](https://dev.to/kai_security_ai/i-scanned-every-server-in-the-official-mcp-registry-heres-what-i-found-4p4m)), and that a critical vulnerability in `mcp-remote`, an OAuth proxy with more than 437,000 downloads, let a malicious server run arbitrary commands on the connecting client ([CVE-2025-6514](https://nvd.nist.gov/vuln/detail/CVE-2025-6514)). Ultra Ledger is a security-focused subregistry: it catalogs servers across the ecosystem and publishes signals you can verify. ## What's in the Ledger Every MCP server indexed by the Ledger has: | Surface | What it shows | | - | - | | **Trust Score** | A 0–100 score derived from eleven security signals. Sigstore-signed and recorded in Rekor's transparency log. | | **Tool inventory** | Every tool the server exposes, with risk level, annotations (`readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint`), and annotation verification status. | | **Permission analysis** | Network, filesystem, env-var, shell, and database access, with scope and risk assessment. | | **Poisoning scan** | Full-schema prompt-injection detection across every JSON schema field, not just `description`. | | **Version history** | Manifest hashes per version, version diffs that surface added or removed tools, permission expansion, and annotation changes. | | **Provenance chain** | Append-only record of every event (indexed, scanned, score change, flag added) signed via Sigstore. | | **Registry coverage** | Which of the 10+ public registries the server appears in. | | **Author profile** | Verified identity (GitHub org, DNS challenge, HTTP challenge), other servers by the same author, reputation score. | ## Trust Score The Trust Score summarizes the scan output as a 0–100 value, calculated from eleven security signals: * **Permission scope:** least-privilege adherence in declared permissions * **Code quality:** linting, dependency hygiene, known vulnerability patterns * **Author reputation:** verified identity, track record, prior servers * **Version stability:** change frequency, breaking changes, suspicious diffs * **Annotation accuracy:** whether self-declared tool annotations match verification * **Tool description safety:** prompt-injection risk across schema fields * **Auth implementation:** OAuth quality, credential management, PKCE * **Spec compliance:** MCP spec adherence, presence of `.well-known/mcp.json` Server Card * **Community adoption:** stars, downloads, cross-registry presence * **Transparency:** license, README, docs, changelog * **Vulnerability history:** historical scan findings Every score calculation is signed via [Sigstore](https://www.sigstore.dev/) and recorded in [Rekor's append-only transparency log](https://docs.sigstore.dev/logging/overview/). Auditors and compliance teams can verify a score was produced by Ultra at the stated time and that the inputs have not been tampered with. ## Annotation verification The MCP spec defines tool annotations (`readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint`) and explicitly states they are **"untrusted unless obtained from a trusted server."** Ultra Ledger verifies them and surfaces one of three badges on every tool: * **Verified:** annotations match what static analysis observes * **Unverified:** no annotations present or verification not yet run * **Mismatch:** declared annotations contradict the tool implementation (e.g., `readOnlyHint: true` on a tool that calls `fs.writeFile`) ## Scanners Each indexed version runs through a fixed scanner pipeline that inspects: * **Manifest and permissions:** declared tools, transport config, and env vars, with least-privilege gaps in the requested permission scope. * **Dependencies:** known CVEs via OSV and the GitHub Advisory Database. * **Secrets:** hardcoded credentials, API keys, and private keys. * **Prompt injection:** full-schema scanning across every JSON field, not just `description`, plus tool descriptions that reference, redirect to, or override other tools. * **Command injection:** taint analysis from tool inputs to dangerous execution sinks. * **Unicode obfuscation:** invisible characters such as zero-width spaces, RTL/LTR overrides, and homoglyphs. * **Annotations and Server Card:** declared tool annotations and `.well-known/mcp.json` claims checked against observed behavior. Every finding maps to an [OWASP MCP Top 10](https://github.com/OWASP/www-project-mcp-top-10) category so security teams can track coverage against a shared framework. ## Browsing the Ledger Navigate to [ledger.ultra.security](https://ledger.ultra.security) and use: * **Search:** filter by score range, tags, transport type, source registry, risk level, or annotation status. Sort by trust score, popularity, recency, or recent flags. * **Server detail:** visit `/s/{slug}` for any server. Shows the Trust Score breakdown, every tool with its risk level and verification badge, full version history with expandable diffs, scan timeline, and provenance chain. * **Registries:** a comparison view of every public MCP registry the Ledger ingests from, with capabilities (search, publishing, security scanning), pricing, and coverage notes. * **Feed:** a chronological stream of recently flagged servers and score changes, filterable by severity. Subscribe via RSS/Atom. * **Transparency:** browse signed events and verify any attestation against Rekor. ## How Ultra uses the Ledger When Ultra evaluates a tool call at runtime, it consults the Ledger Trust Score for the upstream MCP connector alongside its other security checks. Hub deployments cache scores locally and receive webhook updates when a score changes, so policy decisions stay current without round-tripping to the Ledger on every request. Future Ultra Hub releases will let you express score-based policy directly (for example, "block any tool call to a connector with a Trust Score below 60, alert on 60–80"). The current release surfaces Ledger signals as informational context in audit events and the dashboard. ## What the Ledger is not * **Not a runtime enforcer.** The Ledger publishes signals; enforcement happens in Ultra Hub via [Guardrails](/hub/guardrails) and [Governance](/hub/governance). * **Not a hosted registry.** The Ledger indexes servers from other registries and source repositories; it does not host server code or accept direct publishing. * **Not an audit replacement.** Trust Scores raise or lower confidence quickly; they do not replace a manual security review for high-risk integrations. # MCP Best Practices Source: https://docs.ultra.security/concepts/mcp-best-practices Getting the most out of MCP with Ultra AI agents are most effective — and most secure — when they use MCP tool calls instead of shelling out to CLI commands. Ultra makes this easy by acting as the connectivity layer between your agents and MCP connectors. ## Why MCP Over CLI When an AI agent needs to access a file, call an API, or query a database, it has two options: 1. **MCP tool call** — Structured, observable, auditable. Every call flows through Ultra's pipeline and is traced, logged, and metered. 2. **Shell command** — Invisible to security tooling. No audit trail, no policy enforcement, no centralized visibility. MCP tool calls are the better choice for any operation that touches data or external systems. They produce structured results, follow consistent schemas, and give your security team full visibility into what agents are doing. ## Ultra as the Connectivity Layer Without Ultra, connecting MCP agents to connectors is an N×M configuration problem. Every agent (Claude Desktop, Cursor, VS Code, Claude Code, Codex, and Goose) needs to be individually configured for every connector (filesystem, GitHub, Slack, databases). This creates: * **Configuration sprawl** — Duplicate configs across every developer machine * **Security gaps** — No centralized visibility or policy enforcement * **Inconsistency** — Different developers with different connector access Ultra solves this by acting as a single proxy. All agents connect to Ultra, and Ultra connects to all connectors. One configuration, full visibility. **Before Ultra** — every agent connects to every connector (N × M): ```mermaid theme={null} graph LR CA[Agent A] --> S1[Connector 1] CA --> S2[Connector 2] CA --> S3[Connector 3] CB[Agent B] --> S1 CB --> S2 CB --> S3 CC[Agent C] --> S1 CC --> S2 CC --> S3 ``` **With Ultra** — every agent connects to Ultra, Ultra connects to every connector (N + M): ```mermaid theme={null} graph LR CA[Agent A] --> U["Ultra"] CB[Agent B] --> U CC[Agent C] --> U U --> S1[Connector 1] U --> S2[Connector 2] U --> S3[Connector 3] ``` ## Best Practices for Agent Workflows ### Use MCP Tools for All Data Access Every MCP tool call through Ultra generates traces, audit events, and metrics. This gives teams full visibility into what AI agents are doing — which tools they call, how often, latency, error rates, and which users and devices are most active. Prefer MCP tools for: * **File operations** — Read, write, search, and list files via MCP filesystem tools * **API calls** — Use MCP connectors for GitHub, Slack, Notion, and other services * **Database queries** — Route database access through MCP connectors * **Infrastructure operations** — AWS, cloud, and DevOps tools via MCP ### Leverage Tool Namespacing Ultra aggregates tools from all upstream connectors into a single namespace. Agents connecting to Ultra can discover all available tools across all connectors in one place, without needing to know which connector provides which tool. ### Use Resource Reads for Context Gathering MCP resource reads are traced and auditable, just like tool calls. Use them for gathering context from documentation, configurations, and reference data. ### Prefer Structured Tool Calls Over Raw CLI Structured MCP tool calls are: * **Reproducible** — Same inputs produce same outputs * **Auditable** — Full request/response payloads are logged * **Enforceable** — Ultra's guardrails can evaluate and enforce policies on tool calls Raw CLI execution bypasses all of these benefits. ## Observability Benefits Every MCP tool call through Ultra generates: * **Traces** — OpenTelemetry-compatible spans with full request/response payloads * **Audit events** — Security-relevant records with severity, outcome, and principal * **Metrics** — Request counts, latency histograms, error rates, per-connector and per-tool breakdowns This data flows to the [Dashboard](/observability/dashboard) for real-time monitoring and to [Ultra Hub](/hub/overview) for team-wide visibility. ## Security Benefits Ultra's pipeline processes every MCP tool call. This enables: * **Complete audit trails** for compliance (SOC 2, HIPAA, etc.) * **Agent identity tracking** — Know which agent made each request * **[Guardrails](/hub/guardrails)** — Enforce policies on tool calls, validate parameters, rate limit usage None of this is possible with direct CLI execution. ## Configuring Agent Instructions Most AI coding agents support instruction files (like `CLAUDE.md`, `AGENTS.md`, or `.cursorrules`) that guide agent behavior. Use these to enforce MCP-first workflows. ### Example: CLAUDE.md Instructions Add these to your project's `CLAUDE.md` or `~/.claude/CLAUDE.md`: ```markdown theme={null} ## MCP Usage - Use MCP tools for all file access, API calls, and database queries instead of shelling out to CLI commands - All MCP traffic routes through Ultra — do not bypass the proxy - Prefer structured MCP tool calls over raw shell execution for reproducibility and security ## Available MCP Connectors The following MCP connectors are available through Ultra: - **filesystem** — File read/write/search operations - **github** — Repository management, PRs, issues - **notion** — Knowledge base and documentation - **slack** — Team communication ``` ### Example: AGENTS.md Instructions (Codex) Codex reads `AGENTS.md` files from `~/.codex/AGENTS.md` and from each directory in the project tree. Add these to your project's `AGENTS.md`: ```markdown theme={null} ## MCP Policy All external data access MUST go through MCP tools. This ensures: 1. Every operation is traced and auditable via Ultra 2. Security team has visibility into agent activity 3. Guardrails can enforce access policies Do NOT use shell commands for operations that have MCP tool equivalents. ## Available MCP Connectors The following MCP connectors are available through Ultra: - **filesystem** — File read/write/search operations - **github** — Repository management, PRs, issues - **notion** — Knowledge base and documentation - **slack** — Team communication ``` ### Key Principles for Agent Instructions 1. **Be explicit** — List available MCP connectors and their tools so agents know what's available 2. **Set boundaries** — Specify that external data access should go through MCP tools 3. **Reference Ultra** — Mention that MCP traffic routes through Ultra so agents don't try to bypass it 4. **Keep it updated** — As you add new MCP connectors to Ultra, update your instruction files # MCP Security Source: https://docs.ultra.security/concepts/mcp-security Why MCP needs a security layer The Model Context Protocol (MCP) gives AI agents powerful capabilities — file system access, API calls, database queries, code execution. With that power comes risk. Ultra exists to bring visibility and control to MCP communications. ## The MCP Threat Model MCP connectors grant AI agents access to sensitive systems. Without a security layer, several risks emerge: ### Tool Poisoning A malicious or compromised MCP connector could return crafted responses that influence AI agent behavior. For example, a tool response could include instructions that manipulate the agent into calling other tools with unintended parameters. ### Data Exfiltration An AI agent with access to both a filesystem connector and an HTTP connector could read sensitive files and send their contents to an external endpoint — all within a single conversation. ### Privilege Escalation MCP connectors often run with the user's full permissions. An AI agent that convinces a connector to execute arbitrary commands effectively has the user's privileges. ### Uncontrolled Tool Access Without policy controls, any AI agent can call any tool on any connected MCP connector. There's no way to restrict which tools are available to which contexts. ### Compliance Gaps In regulated environments, there's no built-in audit trail for what AI agents do. When an agent modifies a production database or accesses customer data, who knows? ## Ultra Simplifies MCP Connectivity Without Ultra, connecting MCP agents to MCP connectors is an N×M problem — every agent needs to be individually configured for every connector. This creates configuration sprawl, inconsistent security posture, and zero centralized visibility. Ultra acts as a single proxy that all MCP agents connect through. You configure your connectors once in Ultra, and every agent gets access through one connection point. This eliminates per-agent connector configuration and gives you a single place to monitor, audit, and enforce policies on all MCP traffic. ## What Ultra Provides Today Ultra addresses these risks with a **visibility-first** approach: ### Complete Audit Trail Every MCP operation is recorded — tool calls, resource reads, prompt requests. The audit log captures who did what, when, and the full request/response payloads. The audit interceptor fails closed to ensure no operation goes unrecorded. ### Distributed Tracing OpenTelemetry-compatible traces with W3C Trace Context IDs. Correlate MCP operations with your existing observability stack. Know exactly what an AI agent did during a session. ### Real-Time Monitoring The web dashboard provides live visibility into MCP traffic. See which tools are being called, which connectors are active, and whether operations succeed or fail. ### Centralized Visibility Ultra Hub aggregates traces and audit events from all devices across your organization. One dashboard for all MCP activity, across all developers and environments. ### Agent Identity Tracking Ultra identifies which MCP agent (Claude Desktop, Cursor, VS Code, Codex, etc.) made each request, providing context for security analysis. 1. **You can't secure what you can't see.** Most organizations don't even know what their AI agents are doing. Ultra fixes that immediately. 2. **Audit trails have immediate compliance value.** Even without enforcement, a complete audit log satisfies many regulatory requirements. 3. **Observability informs policy.** Understanding actual usage patterns is essential for writing effective policies. Block-first approaches tend to break workflows. 4. **Guardrails add enforcement.** Once you understand your MCP traffic patterns, guardrails let you define and enforce rules — blocking dangerous tool calls, validating parameters, and rate limiting usage. *** Ultra is the security layer that individual MCP connectors and AI agents don't have built in. It's the only platform that gives you complete visibility and policy enforcement across every AI agent, every MCP connector, and every tool call in your organization, while significantly increasing your team's AI-native automation, efficiency, and capabilities. # Pipeline Source: https://docs.ultra.security/concepts/pipeline How Ultra observes and audits every MCP request The pipeline is the core mechanism that gives you visibility into every MCP request and response. Each request flows through Ultra's observability layers — capturing traces, structured logs, audit events, and metrics — before reaching the upstream connector. ## What the Pipeline Captures | Layer | What it captures | Where to view | | - | - | - | | **Trace** | Distributed traces for each request and response | [Traces](/observability/traces) | | **Logging** | Structured request/response logs | [Dashboard](/observability/dashboard) | | **Audit** | Security-relevant events with severity, outcome, and principal | [Audit Log](/observability/audit-log) | | **Metrics** | Request counts, latencies, error rates | [Dashboard](/observability/dashboard) | ## Request and Response Flow ```mermaid theme={null} graph LR Client["MCP Agent"] Pipeline["Pipeline
Trace · Logging · Audit · Metrics"] Server["Upstream Connector"] Client -->|"Request"| Pipeline Pipeline -->|"Request"| Server Server -->|"Response"| Pipeline Pipeline -->|"Response"| Client ``` Every request and response passes through the same set of observability layers, so nothing slips by unrecorded. ## Audit Guarantees The audit layer is designed to block a successful operation if it can't be recorded. This is a deliberate security choice: every completed MCP call must have a corresponding audit record, so compliance teams can rely on completeness. The other layers absorb errors silently — observability should never break your workflow. Beyond observability, the pipeline also enforces policy. [Governance](/hub/governance) admission determines which tool calls are allowed, and [Guardrails](/hub/guardrails) evaluate each request and enforce policy before it reaches the upstream connector. # Secure Configuration Guide Source: https://docs.ultra.security/concepts/secure-configuration-guide How to securely access, configure, operate, and decommission administrative accounts and security-relevant settings for the Ultra agentic security platform This Secure Configuration Guide explains how to securely manage the administrative accounts and security settings of the Ultra platform. It is written for the customer administrators, including federal agency tenants, who provision and manage an Ultra organization, and it supports Ultra's pursuit of FedRAMP 20x Class B authorization (the `SCG-CSO` rule family). The **Ultra Agentic Security Platform** secures agent and MCP tool calls at runtime. Its cloud control plane, **Ultra Hub**, is where organizations, teams, workspaces, identities, governance, and guardrails are managed; enforcement is carried out by the **Ultra Proxy**, which brokers MCP tool calls, applies policy, and syncs telemetry back to Hub. The Proxy runs either **self-hosted** — an instance you run on a developer machine, CI runner, or your own server — or **Ultra-hosted**, enforcement Ultra operates on your behalf; the two differ in guardrail fail behavior, stated in full below. Security configuration is performed in Ultra Hub, which is the focus of this guide. | Field | Value | | - | - | | **Version** | 1.7 | | **Last updated** | September 25, 2026 | | **Owner** | Rob Gutierrez, Director, Security & Business Operations | | **Classification** | Public | | **Contact** | [security@ultra.security](mailto:security@ultra.security) | ## Purpose & scope This guide covers the secure configuration of Ultra Hub: the lifecycle of top-level administrative and privileged accounts, the security-relevant settings those accounts control, the platform's secure defaults, and the programmatic interfaces for viewing, adjusting, exporting, and comparing security settings. It is a configuration reference rather than a replacement for the underlying feature documentation. Where a topic has a canonical home, this guide links to it: [RBAC](/hub/rbac), [Enterprise SSO](/hub/sso), [Directory sync](/hub/scim), [Deploy keys](/hub/deploy-keys), [Admin Log](/hub/admin-log), [Guardrails](/hub/guardrails), [Governance](/hub/governance), and [Identities](/hub/identities). ## How to obtain and use this guide This guide is published publicly in the Ultra documentation and is referenced, with a direct link and short description, in Ultra's FedRAMP Certification Package Overview (CPO). You can also request the current version from `security@ultra.security`. Use it as the authoritative reference when standing up a new Ultra organization, hardening an existing one, or conducting an authorization review. **Applying this guide (customer responsibility).** Customers implement it by mapping each recommended setting to their own tenant in **Settings > Security**, following the top-level administrative account lifecycle below, and then using the [comparison utility](#settings-comparison) to verify their configuration against Ultra's recommended baseline. Ultra is responsible for the platform controls described here; the customer (agency) is responsible for applying the recommended settings, managing their own administrative accounts, and remediating deviations. ## Account model & administrative roles Ultra Hub enforces role-based access control (RBAC). Four roles apply to people, in decreasing order of privilege: **Owner**, **Admin**, **Member**, **Viewer**. A user's role at the organization level cascades to all teams and workspaces. **Beacon** is a separate proxy identity rather than a rung on that ladder; it is described below the table. | Role | Type | Security-relevant capability | | - | - | - | | **Owner** | Top-level administrative account | Full control of the organization including billing and settings. Only role that can delete the organization and the only role that can promote another user to Owner. The user who creates the organization is the first Owner. | | **Admin** | Privileged account | Day-to-day administration: manage members and roles (up to Admin), teams, workspaces, proxies, deploy keys, SSO and SCIM (System for Cross-domain Identity Management), governance rules, and guardrails. Can view the Admin Log, but cannot alter or delete its records. Cannot delete the org or assign Owner. | | Member | Standard user | Read access to org, team, and workspace, including read-only visibility of guardrail configuration. Can update workspaces, and register, link, read, update, and sync proxies in them, not only proxies they own. Cannot unlink, archive, or delete a proxy they do not own; those require a permission the role does not hold. Cannot manage users or org settings, cannot configure guardrails, and cannot view the Admin Log. | | Viewer | Read-only | Read-only visibility into the organization's teams and workspaces. Holds `org:read`, which also reaches the deploy-key inventory (metadata only, never key material) and the directory-sync endpoint, and can link a proxy. Note what that endpoint returns: its response body is the organization's active SCIM self-service setup-link URL, not a sync status, so a Viewer can read a link that provisions directory sync. Cannot update workspaces, manage users, or change settings. | **Roles are scoped by where the membership is granted, and an organization-level role reaches everything.** A membership recorded at the organization level resolves for every team and workspace in that organization, so an organization-level Member reaches every workspace, not only the ones they were individually added to. Grant team- or workspace-level memberships where you need a narrower blast radius. **One escalation path is worth knowing.** Assigning a proxy owner requires only `workspace:manage_gateways`, which a Member holds, and it applies to proxies linked by a deploy key. A Member can therefore make themselves the owner of such a proxy and then reach unlink, archive, and delete through the owner path, despite not holding the delete permission directly. Treat Member as a privileged role on any workspace that carries deploy-key-linked proxies. **Beacon** is a proxy-only identity that proxies use for sync operations (traces, heartbeat). Its permissions are a subset of Viewer's, so it is not a lower rung of the human ladder in the sense of holding anything a person does not. It is a separate credential class for a different reason: it is minted for a proxy rather than assigned to a person, and it carries no dashboard access and cannot read organization data. Treat it as a machine credential rather than as a reduced user account. For FedRAMP purposes, the **Owner** role is the top-level administrative account that controls enterprise access to the entire offering, and the **Admin** role is the privileged account. ## Securing top-level administrative accounts ### Provisioning & secure access Owner and Admin accounts authenticate through one of the following methods: * **Passwordless web authentication**: email plus a one-time verification code. No stored passwords to rotate. Sessions are held in secure cookies and expire, requiring re-authentication. * **Enterprise SSO (single sign-on, SAML 2.0 or OIDC)**: delegate authentication to your identity provider (IdP), such as Okta, Entra ID, Google Workspace, JumpCloud, PingOne, OneLogin, AD FS, Auth0, or Rippling. Recommended for all administrative accounts so that IdP-side multi-factor authentication (MFA), conditional access, and session policy apply. See [Enterprise SSO](/hub/sso). * **CLI authentication**: `ultra login` uses the OAuth2 PKCE (Proof Key for Code Exchange) flow via the browser; tokens are stored in the local config and refreshed automatically. * **Deploy keys**: long-lived `dk_` tokens for headless and CI proxies only. These authenticate a proxy, not an administrator. See [Deploy keys](/hub/deploy-keys). **Brute-force protection.** Ultra Hub enforces account lockout after repeated failed authentication attempts. SSO login flows carry CSRF (cross-site request forgery) state parameters and expire after 10 minutes. Self-registration into an existing organization is not permitted; access requires an invitation, SSO just-in-time (JIT) provisioning, or SCIM provisioning. Require Enterprise SSO for all Owner and Admin accounts and enable **Enforce SSO** for your domain so administrators cannot fall back to email-code login. Ensure phishing-resistant MFA is enforced at your identity provider. Email one-time codes are not phishing-resistant on their own, which is why FedRAMP expects privileged accounts to authenticate through an IdP that enforces a phishing-resistant factor. ### Configuring administrative accounts Owners and Admins configure enterprise access from **Settings > Security** and the **Settings > Members** tab: * **Roles (RBAC).** Assign the least-privilege role. Only Owners may assign or remove Owner; Admins may assign up to Admin. The last Owner cannot be demoted or removed. See [RBAC](/hub/rbac). * **Enterprise SSO.** Generate a setup link, complete the guided IdP wizard, **Test Connection**, then optionally **Enforce SSO** for matching domains. * **Directory Sync (SCIM 2.0).** Automate provisioning and deprovisioning and group-to-team mapping so account lifecycle follows your corporate directory. See [Directory sync](/hub/scim). * **Just-in-Time (JIT) provisioning.** Optionally auto-create Members on first SSO login; disable it if you require pre-provisioning via SCIM or invitation. ### Operating administrative accounts * **Admin Log (Owner and Admin only).** Review the security event log for member provisioning and removal, SSO authentication (including SSO login failures), guardrail configuration changes, governance posture changes, and a subset of organization, workspace, and proxy events. Removing or disabling a security control is logged at *warning* severity. Coverage is narrower than the page's event list implies; see the gaps below before relying on it as a sole record. See [Admin Log](/hub/admin-log). * **Identities.** Monitor every principal generating activity, its assurance tier (Authenticated, Device-attributed, Unattributed, or No activity), tool-call volume, guardrail blocks, and anomaly findings. Bring unattributed traffic under management or block it via Governance. See [Identities](/hub/identities). * **Least privilege & separation.** Keep the number of Owners minimal; use Admin for routine administration and Member or Viewer for everyone else. **Known gaps in administrative audit coverage.** Two cases are not fully captured in the Admin Log today, and agencies conducting an authorization review should account for them: * Changing a member's **organization** role does not currently emit an Admin Log event. Team-scoped role changes do. * Events driven by SCIM directory sync are recorded without actor, target, and IP address, because the SCIM webhook runs outside the request context that supplies them. Both are tracked for remediation. Until they land, treat your identity provider's own logs as the authoritative record for org role changes and SCIM-driven lifecycle events. ### Decommissioning administrative accounts Do not overlook decommissioning. Every administrative account and credential must have a clean removal path. * **SCIM deprovisioning (preferred).** Deleting a user in your IdP soft-deletes them in Ultra: the authentication identity is deactivated first (blocking login immediately), the account is marked inactive, and audit trails and memberships are preserved. Reactivation restores prior access if the user returns. * **Manual removal.** Owners and Admins can remove a member or change their role from the **Settings > Members** tab; changes take effect immediately. Deactivated users are blocked from SSO login even if their IdP session is still valid. * **Manual removal is not terminal while JIT is enabled.** If JIT provisioning is on for a domain, a manually removed user is re-created as a Member on their next SSO login. Remove the user at the IdP, or disable JIT, when the removal is meant to be permanent. * **Owner protection.** The last Owner of an organization cannot be demoted or removed, preventing accidental loss of administrative control. Transfer Owner to a successor before offboarding an Owner. * **Deploy key revocation.** Revoke deploy keys from **Settings > Security**. Proxies using a revoked key lose Hub authentication on their next sync, which by default is within 60 seconds. Rotate keys periodically and scope them narrowly per workspace. * **Proxy decommissioning.** Unlink or archive proxies that are retired; use Governance to block a decommissioned internal MCP connector organization-wide. Revocation stops Hub authentication, not traffic. A proxy whose deploy key has been revoked continues brokering MCP calls and enforcing its already-resolved guardrails; what it loses is telemetry sync, heartbeat, and configuration pull. Revocation does not prevent the proxy from starting either: `ultra start` is exempt from the CLI's Hub-auth gate, and the local check that does exist, on Ultra's own management tools, reads the credential's format rather than its validity. To stop traffic, decommission the proxy itself. ## Security-relevant settings and their implications The following settings are operable only by top-level administrative (Owner) and privileged (Admin) accounts. Recommended secure values are shown in the final column and match the [machine-readable baseline](#machine-readable-secure-configuration-guide), so a tenant configured from this table reaches exit 0 on the baseline's automated checks, leaving the manual-review items for an administrator to confirm. A tenant that deliberately runs Google sign-in is the documented exception: the baseline records it as a deviation, so such a tenant exits 1 by design and should carry the approval alongside its report. One caveat: deploy-key names are not unique per organization, and the recommended per-workspace scoping makes repeated names likely (a `ci` key in each workspace). The comparison utility reports an export it cannot read key by key as `NO DATA` rather than guessing, so a tenant following this table may still exit 1 until its key names are made distinct. | Setting | Who can change | Security implication | Recommended secure value | | - | - | - | - | | Enterprise SSO connection | Owner / Admin (`org:update`) | Central authentication path for the org; misconfiguration can lock out users or weaken auth. | Configured to your IdP; tested before enforcement. | | Enforce SSO | Owner / Admin (`org:update`) | Blocks email-code login for matching domains, forcing all auth through the IdP and its MFA. | Enabled for corporate domain(s). | | JIT provisioning (Enterprise SSO) | Owner / Admin (`org:update`) | Auto-creates accounts on first SSO login; convenient but reduces pre-authorization control, and re-creates manually removed users. | Disabled. Where enabled deliberately, record it as an approved deviation. | | SCIM directory sync | Owner / Admin (`org:update`) | Drives account lifecycle from your IdP; webhooks are HMAC-SHA256 signed with 5-minute replay protection. | Enabled; secret configured during SSO setup. | | Default provisioned role | Owner / Admin (`org:update`) | The role every identity that SCIM **or** enterprise SSO creates receives; a privileged default grants privilege to everyone the identity provider provisions. | `Member` or tighter. Ultra clamps the setting to `beacon`, `viewer`, or `member`, so it can never grant Admin or Owner. | | Google sign-in | Owner / Admin (`org:update`) | A second sign-in path alongside Enterprise SSO, with its own enforcement and JIT settings. Leaving it unenforced permits email-code fallback on domains it covers. | Disabled, so Enterprise SSO is the single administrative authentication path. Where it is deliberately enabled, record it as an approved deviation and enforce it for your corporate domain(s) with its JIT provisioning off. | | Google JIT provisioning | Owner / Admin (`org:update`) | Independent of the Enterprise SSO JIT setting; auto-creates accounts for anyone in the configured Google domains. | Disabled. | | Member roles (RBAC) | Owner (any) / Admin (up to Admin), `org:manage_members` | Determines privilege; over-assignment expands blast radius. | Least privilege; no more than three Owners. | | Deploy keys | Owner / Admin (`org:update`) | Long-lived proxy credentials; if leaked, allow proxy authentication to a workspace. | Per-workspace scope, expiry set, rotated, revoked when unused. | | Guardrails (built-in & custom) | Owner / Admin | Enforce policy on every tool call. The recommended built-in guardrails are enabled by default; disabling one removes protection (logged at warning severity). | Keep the recommended built-ins enabled. Parameter Validation & Input Sanitization: Block. Credential & Secret Protection: Block. In-Line Authorization & Destructive Action Blocking: Block. PII (personally identifiable information) & Personal Data Protection: Redact. Tool & Server Isolation: Block. Rate Limiting: Block, noting that it is enforced on self-hosted proxies only (see [Guardrail fail behavior](#guardrail-fail-behavior)). Circuit Breaker is not part of the recommended set. | | Anomaly detection | Owner / Admin | Continuously baselines identity and tool-call behavior and surfaces anomaly findings; disabling it reduces detection coverage (logged at warning severity). | Enabled by default; keep on and review findings on the Identities monitor. | | Governance | Owner / Admin | Optional, customizable reachability control. `default_allow` admits unmatched calls; `default_deny` is zero-trust. A narrower scope can add restrictions but never remove them: the effective posture is the stricter of the organization and workspace settings, so a workspace cannot sit at `default_allow` beneath an organization `default_deny`. | `default_deny` on sensitive workspaces. | | Admin Log access | Not configurable | Privileged audit material; exposure would reveal security operations. | Read access is restricted to Owner and Admin by RBAC, and is not a setting an administrator can change. | | Organization deletion | Not configurable | Irreversible removal of the organization. | Restricted to Owner by RBAC, and is not a setting an administrator can change. | ### Privileged (Admin) account settings Beyond the Owner-exclusive controls (org deletion, Owner promotion, billing), privileged Admin accounts can operate the following security-relevant settings. Each should be exercised under least privilege: * Create, update, and delete teams and workspaces (environment isolation boundaries). * Manage proxies: register, link, unlink, archive, delete. * Configure and scope **guardrails** and **governance rules** at organization, workspace, or proxy level. A narrower scope can add restrictions but never remove them. For guardrails, the strictest enabled row wins, and scope (organization, then workspace, then proxy) breaks a tie only between rows of equal strictness; a row disabled at a broader scope falls through to a more specific enabled row, and where every row is disabled the most specific one stands as an explicit off-state. Rate Limiting is the exception: its rules from different scopes all apply concurrently, a narrower cap adding to a broader ceiling rather than replacing it. Governance *posture* resolves on the same principle: the effective posture is the stricter of the organization and workspace settings. Governance *rules* are not resolved to a single winner at all: every enabled rule is evaluated, and a matching block denies the call. * Manage members and roles up to Admin, and manage SSO, SCIM, and deploy-key configuration. ## Secure defaults A newly provisioned Ultra organization starts from the following baseline: * **Passwordless authentication** by default: no stored passwords; account lockout on repeated failures. * **No open self-registration**: joining an existing org requires an invitation, SSO JIT, or SCIM. * **SCIM webhooks** require HMAC-SHA256 signatures with timestamp and replay protection; unsigned webhooks are rejected. * **RBAC least privilege**: new invited users default to the role specified in the invitation; the Admin Log is restricted to Owner and Admin. * **Built-in guardrails enabled**: the recommended built-in guardrails are on by default, each in its recommended mode, and can be tuned or extended with custom guardrails. * **Anomaly detection enabled**: Ultra continuously baselines identity and tool-call behavior and surfaces anomaly findings on the Identities monitor out of the box. **Built-in guardrails are enabled by default.** Ultra ships a catalog of built-in guardrails covering the most common MCP threats: Parameter Validation & Input Sanitization, Credential & Secret Protection, In-Line Authorization & Destructive Action Blocking, PII & Personal Data Protection, Tool & Server Isolation, Rate Limiting, and Circuit Breaker. The recommended built-ins are enabled by default on a newly provisioned organization, each in its recommended mode (for example, Redact for PII & Personal Data Protection). You can tune enforcement modes, add custom guardrails, and scope them at organization, workspace, or proxy level. Disabling a built-in is a deliberate action, restricted to Owner and Admin and logged at warning severity. **Deviations and review.** The ability to weaken a control (for example, disabling a guardrail or lowering an enforcement mode) is restricted to Owner and Admin accounts and is recorded in the Admin Log at warning severity. Approved deviations from the recommended baseline should be documented by the administrator, and the baseline itself is reviewed and updated when Ultra's recommended security defaults change. **Recommended hardening after provisioning:** enable Enterprise SSO with **Enforce SSO**, enable the recommended built-in guardrails and tune their enforcement modes (adding custom guardrails where useful), and use Governance to restrict which MCP connectors, tools, and agents are reachable, setting sensitive workspaces to `default_deny`. ### Guardrail fail behavior Guardrails fail closed on the local proxy: if a request-phase evaluator errors, panics, or is missing, the request is blocked. Four limits are worth stating precisely, because an authorization review will test them: * **Hosted proxies skip stateful evaluators.** The hosted (Postgres and OAuth) proxy has no shared counter store, so Rate Limiting and Circuit Breaker cannot be evaluated there. A guardrail of that type is skipped rather than failing closed. * **Fail-closed applies to blocking modes.** A response-phase evaluator error blocks only when the configured action is block. Guardrails in redact or monitor mode keep their allow-with-signal behavior. Three response-phase paths block regardless of the configured mode: a truncated response scan, a redact-mode decision whose redaction fails, and an unrecognized mode. * **Response-phase panics are not recovered.** The request-phase guarantee above does not extend to responses: a panic during response evaluation propagates rather than blocking. * **Startup warnings can indicate missing protection.** If Ultra cannot initialize local enforcement, it can continue forwarding traffic without guardrail or governance protection and report a startup warning. An authorization review should test this case explicitly. ## Programmatic access, export & machine-readable guidance ### API to view and adjust security settings Ultra Hub exposes REST APIs that let agencies view and adjust access- and policy-related security settings, and integrate them into their own automation and authorization-monitoring workflows: | Setting | API capability | | - | - | | Member roles (RBAC) | `PATCH /api/v1/organizations/:org/members/:member` for programmatic role assignment. | | Guardrails | API to view and adjust guardrail configuration and enforcement mode. | | Governance | API to view and adjust governance rules and posture. | | Enterprise SSO | API for SSO connection configuration. | | SCIM directory sync | API to enable directory sync. The read endpoint returns the organization's active self-service setup-link URL rather than a sync status, and requires only `org:read`; see the Viewer row above. | The Ultra Proxy's local configuration (`config.yaml`) is intentionally not mutable via public API, so enforced proxy policy stays consistent regardless of the deployment environment. **Authentication & authorization.** Administrative API calls require an authenticated Hub principal. Three credentials are accepted: a session token, sent as `X-Session-Token` or the session cookie; an OAuth2 access token; or a CLI API token, the latter two sent as `Authorization: Bearer`. Any of the three carries the caller's real memberships, so the same RBAC model as the UI applies. Deploy keys authenticate a proxy, not an administrator: they carry a workspace-scoped credential with no memberships, so they cannot call these endpoints. Permissions differ by endpoint, and reads are not uniformly restricted: * Role changes require `org:manage_members`. * Enterprise SSO endpoints require `org:update`, reads included. * SCIM and deploy-key **writes** require `org:update`, but `GET /api/v1/organizations/{org}/scim` and `GET /api/v1/organizations/{org}/deploy-keys` require only `org:read`. A Viewer can therefore read the deploy-key inventory (metadata only, never key material) and, from the SCIM endpoint, the active directory-sync setup-link URL rather than a mere status. Take this into account when assigning the Viewer role. **Example: change a member's role.** An Owner or Admin can update a member's role with a single call: ```http theme={null} PATCH /api/v1/organizations/{org}/members/{member} Content-Type: application/json { "role": "admin" } ``` This returns the updated membership with its new role. Note the audit gap described above: this call does not currently emit an Admin Log event. **API best practices:** rotate deploy keys and API credentials periodically, grant the least privilege required for each integration, and monitor the Admin Log for API-driven configuration changes. ### Export of security settings Ultra Hub supports one-click export in both **CSV and JSON** on its list and log surfaces, including Traffic, Audit, Connectors, Tools, Agents, Devices, Identities, Anomaly scans, the Admin Log, and **Settings > Security**. Both formats are available wherever export exists. Two exports matter for this guide: * **Settings > Security** exports the organization's security posture as a normalized `Category, Setting, Value` table covering allowed domains, sign-in methods (magic links, Google, Enterprise SSO), identity provisioning, SCIM, and deploy-key metadata. Secret material (SSO connection secrets, setup-link URLs, deploy-key hashes) is deliberately excluded. * **Identities** exports one row per principal with a Role column, which is how Owner accounts are counted. Guardrail and governance configuration are **not** exportable by customers today. Both are verified in the Hub UI, and the comparison utility below reports them for manual review rather than guessing. Export coverage for these surfaces is tracked separately. ### Machine-readable Secure Configuration Guide A machine-readable companion to this guide, listing the recommended secure default settings for top-level administrative and privileged accounts, is provided in JSON below. Each entry declares how it can be verified: | `verification.method` | Meaning | | - | - | | `export` | Comparable against a customer-reachable Hub export; `export` and `field` name the source and the exact field. | | `manual` | No customer-reachable export exists today; verify in the Hub page named in `source`. | | `platform` | Platform behavior a customer cannot configure. Reported for information only. | For most entries `verification.field` is the exact `Category / Setting` key looked up in the export, and that includes any entry carrying `maxRole`: its field is read before the ranking is applied, so it is a lookup key like any other, and renaming it turns a passing check into `NO DATA`. What `maxRole` changes is the comparison, not the lookup. Two entries are different. Neither can be answered by a single exported row, so each is checked by dedicated logic and its field string is descriptive only, meaning editing it changes nothing but the printed `source:` line: | Entry | What is actually read | | - | - | | `deploy_keys.expiry_required` | The declared key count plus every per-key `Expires` row, cross-checked against each other. | | `rbac.owner_count` | The **Role** column of the Identities export, so `field` names a column rather than a `Category / Setting` key. Compared against `maxOwners`. | `maxOwners` and `maxRole` are the thresholds those comparisons use, and are part of the contract if you reimplement this baseline: `maxOwners` is an upper bound on a count, `maxRole` a least-privilege ceiling ranked `beacon` \< `viewer` \< `member`. The `operableBy` field records who can change a setting: `owner`, `owner_admin`, or `system_default` for platform behavior. **`verification.notApplicableWhen` (optional).** Some recommendations are moot for a given tenant. Enterprise SSO's JIT setting cannot provision anyone if Enterprise SSO is not enabled, and Google's JIT setting cannot if Google sign-in is not enabled. Where that is true, the entry names the field and value that makes it moot: ```json theme={null} "notApplicableWhen": { "field": "Sign-in · Enterprise SSO / Enabled", "equals": false } ``` The condition applies **only when the named field is itself observed to hold that value**. A field that is absent, blank, or ambiguous in the export does not satisfy it, so a recommendation can never be switched off by data that is merely missing. If you reimplement this baseline in your own tooling, preserve that property: it is the difference between "we saw that this does not apply" and "we could not tell". Copy the baseline directly from this page; it is also included in Ultra's FedRAMP Certification Package. ```json theme={null} { "document": "Ultra Secure Configuration Guide: machine-readable baseline", "version": "1.7", "lastUpdated": "2026-09-25", "owner": "Rob Gutierrez, Director, Security & Business Operations", "cloudServiceOffering": "Ultra Agentic Security Platform", "scopeNote": "These settings are configured in Ultra Hub, the cloud control plane of the Ultra Agentic Security Platform.", "verificationMethods": { "export": "Comparable against a customer-reachable Hub export. 'export' names which one.", "manual": "No customer-reachable export exists today; verify in the Hub UI page named in 'source'.", "platform": "Platform behavior a customer cannot configure. Informational only." }, "notApplicableWhen": "Optional. Names another exported field and the value that makes this recommendation moot for a tenant. It applies only when that field is itself observed to hold that value, so a missing or unreadable row can never turn a recommendation off.", "recommendedSecureDefaults": [ { "key": "auth.method", "title": "Enterprise SSO configured", "recommendedValue": true, "operableBy": "owner_admin", "rule": "SCG-CSO-RSC", "verification": { "method": "export", "export": "security-settings", "source": "Settings > Security", "field": "Sign-in · Enterprise SSO / Enabled" }, "rationale": "Delegate Owner/Admin authentication to the corporate identity provider so its phishing-resistant MFA and session policy apply." }, { "key": "sso.enforce", "title": "Enforce SSO for domain", "recommendedValue": true, "operableBy": "owner_admin", "rule": "SCG-CSO-RSC", "verification": { "method": "export", "export": "security-settings", "source": "Settings > Security", "field": "Sign-in · Enterprise SSO / Enforce SSO", "notApplicableWhen": { "field": "Sign-in · Enterprise SSO / Enabled", "equals": false, "reason": "Enterprise SSO is not enabled, so there is no connection to enforce" } }, "rationale": "Blocks email-code fallback for corporate domains, forcing administrative authentication through the identity provider." }, { "key": "sso.jit_provisioning", "title": "Just-in-Time provisioning", "recommendedValue": false, "operableBy": "owner_admin", "rule": "SCG-CSO-RSC", "verification": { "method": "export", "export": "security-settings", "source": "Settings > Security", "field": "Sign-in · Enterprise SSO / JIT provisioning", "notApplicableWhen": { "field": "Sign-in · Enterprise SSO / Enabled", "equals": false, "reason": "Enterprise SSO is not enabled, so it provisions no accounts" } }, "rationale": "Disable where SCIM or invitation pre-provisioning is required, so accounts are authorized before they exist. Where JIT is deliberately enabled, record it as an approved deviation." }, { "key": "sso.google.enabled", "title": "Google sign-in", "recommendedValue": false, "operableBy": "owner_admin", "rule": "SCG-CSO-RSC", "verification": { "method": "export", "export": "security-settings", "source": "Settings > Security", "field": "Sign-in · Google / Enabled" }, "rationale": "Prefer Enterprise SSO as the single administrative authentication path. This row is always emitted, so it also verifies the premise the two Google recommendations below depend on: every notApplicableWhen anchor is itself a checked entry. A tenant that deliberately runs Google sign-in records this as an approved deviation and satisfies the two rows below; the settings table recommends the same." }, { "key": "sso.google.enforce", "title": "Enforce Google sign-in for domains", "recommendedValue": true, "operableBy": "owner_admin", "rule": "SCG-CSO-RSC", "verification": { "method": "export", "export": "security-settings", "source": "Settings > Security", "field": "Sign-in · Google / Enforce for domains", "notApplicableWhen": { "field": "Sign-in · Google / Enabled", "equals": false, "reason": "Google sign-in is not enabled" } }, "rationale": "Where Google sign-in is enabled for a corporate domain, enforce it so users on that domain cannot fall back to email-code login." }, { "key": "sso.google.jit_provisioning", "title": "Google JIT provisioning", "recommendedValue": false, "operableBy": "owner_admin", "rule": "SCG-CSO-RSC", "verification": { "method": "export", "export": "security-settings", "source": "Settings > Security", "field": "Sign-in · Google / JIT provisioning", "notApplicableWhen": { "field": "Sign-in · Google / Enabled", "equals": false, "reason": "Google sign-in is not enabled, so it provisions no accounts" } }, "rationale": "Google sign-in carries its own JIT setting, independent of Enterprise SSO. Left on, it auto-creates accounts for anyone in the configured domains and re-creates manually removed users." }, { "key": "scim.directory_sync", "title": "SCIM 2.0 directory sync", "recommendedValue": true, "operableBy": "owner_admin", "rule": "SCG-CSO-RSC", "verification": { "method": "export", "export": "security-settings", "source": "Settings > Security", "field": "Directory Sync (SCIM) / Configured" }, "rationale": "Drives account provisioning and deprovisioning from the corporate directory. SCIM webhooks are HMAC-SHA256 signed with replay protection." }, { "key": "provisioning.default_role", "title": "Default provisioned role", "recommendedValue": "member or lower", "operableBy": "owner_admin", "rule": "SCG-CSO-RSC", "verification": { "method": "export", "export": "security-settings", "source": "Settings > Security", "field": "Identity Provisioning / Default provisioned role" }, "rationale": "Least privilege at provisioning time. Every identity that SCIM or enterprise SSO creates receives this role. Ultra clamps the setting to beacon, viewer, or member and resolves anything else to member, so it can never grant Admin or Owner; the check confirms the effective value is member or a tighter role rather than pinning one exact value, so a tenant hardened to viewer or beacon still passes.", "maxRole": "member" }, { "key": "deploy_keys.expiry_required", "title": "Deploy key expiry", "recommendedValue": true, "operableBy": "owner_admin", "rule": "SCG-CSO-RSC", "verification": { "method": "export", "export": "security-settings", "source": "Settings > Security", "field": "Deploy Keys / Configured keys + Deploy Key · / Expires" }, "rationale": "Scope keys per workspace, set an expiry, rotate periodically, and revoke unused keys. A key exported with an expiry of 'Never' has none set; an organization with no deploy keys satisfies this recommendation." }, { "key": "rbac.owner_count", "title": "Number of Owner accounts", "recommendedValue": "no more than 3", "maxOwners": 3, "operableBy": "owner", "rule": "SCG-CSO-RSC", "verification": { "method": "export", "export": "identities", "source": "Identities", "field": "Role" }, "rationale": "Least privilege. Keep top-level administrative accounts to the minimum necessary while retaining a recovery path if one Owner is unavailable." }, { "key": "guardrails.builtin_enabled", "title": "Recommended built-in guardrails enabled", "recommendedValue": true, "operableBy": "owner_admin", "rule": "SCG-CSO-SDF", "verification": { "method": "manual", "source": "Guardrails" }, "rationale": "The recommended built-in guardrails are enabled by default on a newly provisioned organization; keep them on and tune modes as needed. Disabling one is restricted to Owner/Admin and logged at warning severity." }, { "key": "guardrails.parameter_validation.mode", "title": "Parameter Validation & Input Sanitization mode", "recommendedValue": "block", "operableBy": "owner_admin", "rule": "SCG-CSO-SDF", "verification": { "method": "manual", "source": "Guardrails" }, "rationale": "One of the six catalog-recommended built-ins. Blocks malformed and manipulated tool parameters before they reach an upstream connector." }, { "key": "guardrails.credential_protection.mode", "title": "Credential & Secret Protection mode", "recommendedValue": "block", "operableBy": "owner_admin", "rule": "SCG-CSO-SDF", "verification": { "method": "manual", "source": "Guardrails" }, "rationale": "Block access to credential files, secrets in parameters, and cloud metadata endpoints." }, { "key": "guardrails.pii_protection.mode", "title": "PII & Personal Data Protection mode", "recommendedValue": "redact", "operableBy": "owner_admin", "rule": "SCG-CSO-SDF", "verification": { "method": "manual", "source": "Guardrails" }, "rationale": "Redact mode is the recommended starting configuration for this guardrail." }, { "key": "guardrails.destructive_action_blocking.mode", "title": "In-Line Authorization & Destructive Action Blocking mode", "recommendedValue": "block", "operableBy": "owner_admin", "rule": "SCG-CSO-SDF", "verification": { "method": "manual", "source": "Guardrails" }, "rationale": "Block destructive tool names and dangerous SQL or shell operations before they reach target systems." }, { "key": "guardrails.tool_server_isolation.mode", "title": "Tool & Server Isolation mode", "recommendedValue": "block", "operableBy": "owner_admin", "rule": "SCG-CSO-SDF", "verification": { "method": "manual", "source": "Guardrails" }, "rationale": "Prevent cross-server exfiltration by blocking values read from one server from being written to another in the same session." }, { "key": "guardrails.rate_limiting.mode", "title": "Rate Limiting mode", "recommendedValue": "block", "operableBy": "owner_admin", "rule": "SCG-CSO-SDF", "verification": { "method": "manual", "source": "Guardrails" }, "rationale": "One of the six catalog-recommended built-ins. Bounds per-connector request rates to prevent resource exhaustion. Not evaluated on hosted proxies, which have no shared counter store." }, { "key": "anomaly_detection.enabled", "title": "Anomaly detection", "recommendedValue": true, "operableBy": "owner_admin", "rule": "SCG-CSO-SDF", "verification": { "method": "manual", "source": "Identities" }, "rationale": "Enabled by default. Ultra continuously baselines identity and tool-call behavior and surfaces anomaly findings on the Identities monitor. Keep it on and triage findings; disabling it reduces detection coverage and is logged at warning severity." }, { "key": "governance.posture", "title": "Governance posture", "recommendedValue": "default_deny", "operableBy": "owner_admin", "rule": "SCG-CSO-SDF", "appliesTo": "sensitive workspaces", "verification": { "method": "manual", "source": "Governance" }, "rationale": "Zero-trust reachability for sensitive workspaces: only explicitly allowed connectors, tools, and agents reach upstreams. A narrower scope can add restrictions but never remove them, and the effective posture is the stricter of the organization and workspace settings, so an organization on default_allow with sensitive workspaces on default_deny satisfies this recommendation." }, { "key": "guardrails.fail_mode", "title": "Guardrail fail behavior", "recommendedValue": "fail_closed", "operableBy": "system_default", "rule": "SCG-CSO-SDF", "verification": { "method": "platform", "source": "Platform behavior" }, "rationale": "On the local proxy, request-phase evaluator errors, panics, and missing evaluators block the request. Scope limits are documented in the guide." }, { "key": "org.self_registration", "title": "Open self-registration", "recommendedValue": false, "operableBy": "system_default", "rule": "SCG-CSO-SDF", "verification": { "method": "platform", "source": "Platform behavior" }, "rationale": "Joining an existing organization requires an invitation, SSO JIT, or SCIM. There is no open self-registration." }, { "key": "admin_log.access", "title": "Admin Log access", "recommendedValue": "owner_admin_only", "operableBy": "system_default", "rule": "SCG-CSO-RSC", "verification": { "method": "platform", "source": "Platform behavior" }, "rationale": "Privileged audit material restricted to Owner and Admin by RBAC." }, { "key": "org.deletion", "title": "Organization deletion", "recommendedValue": "owner_only", "operableBy": "system_default", "rule": "SCG-CSO-RSC", "verification": { "method": "platform", "source": "Platform behavior" }, "rationale": "Irreversible removal of the organization is restricted to Owner by RBAC and is not customer-configurable." } ], "maxOwners": "Optional. Upper bound for a counted recommendation, used by rbac.owner_count.", "maxRole": "Optional. Least-privilege ceiling for a role-valued recommendation, ranked beacon < viewer < member." } ``` ### Settings comparison Ultra provides a comparison utility (`ultra_scg_compare.py`, Python 3, no external dependencies) that ingests a tenant's exports and reports each recommended setting as matching, deviating, or requiring manual review. In Ultra Hub, open **Settings > Security** and use **Export** (CSV or JSON). Then open **Identities** and export it the same way, with no filters applied: the export honors whatever filters are active on the page, and a filtered roster undercounts Owner accounts. Supply both files unmodified: renaming a column or editing a value makes the affected recommendation unverifiable, and the utility reports that rather than guessing. Deploy-key expiries are read as `Never` or as the RFC 3339 timestamp the export emits. Copy the recommended baseline (`Ultra-SCG-baseline.json`) and the comparison script (`ultra_scg_compare.py`) from the collapsible blocks on this page. Both are also included in Ultra's FedRAMP Certification Package. ```bash theme={null} python ultra_scg_compare.py --baseline Ultra-SCG-baseline.json \ --security-settings security-settings.csv \ --identities identities.csv ``` Add `--json` for a machine-readable report suitable for continuous-monitoring pipelines. Every automated check reports whether it **observed** a value, separately from whether that value matched. That split is what the four results mean: | Result | Meaning | | - | - | | `OK` | A value was observed and it matches the recommendation. This is the only passing result, and it is unreachable without an observation. | | `DEVIATION` | A value was observed and it does not match. Prints the current and recommended values, plus a reason when the value is one the utility does not recognize. | | `NO DATA` | No judgement is possible, either because no value was observed or because a value was read that the check cannot place. Every line states its own cause. Broadly: the export was not supplied; the value was absent, blank, or contradicted by a duplicate row; the export's shape did not match what the check reads, such as a renamed column or a key count that disagrees with the rows present; or the value falls outside the set the check can rank, in which case the line says so and tells you how to read it. | | `N/A` | The recommendation does not apply to this tenant, established from a value that **was** observed (for example, Google's JIT setting when the export shows Google sign-in is not enabled). | `NO DATA` is not a pass, which is why it is grouped with deviations in the exit code. `N/A` is neither, and does not affect it. Settings with no customer-reachable export are listed under manual review, and platform behaviors are listed for information only. Neither affects the exit code. Exit codes: `0` when every automated check matched, `1` when any deviated or could not be verified, and `2` when the baseline or an export was missing, unreadable, or not in the expected format. Remediate deviations in **Settings > Security**. Sample output, from a tenant with Enforce SSO turned off and Google sign-in unused. It is a verbatim run of the script above, leading blank line included, against a **Settings > Security** and an **Identities** export in the shape Hub produces, so the values shown (`'false'`, not `'No'`) are the ones you will see in your own run: ```text theme={null} Ultra SCG settings comparison ============================================================================== AUTOMATED (verified from an export) [OK ] Enterprise SSO configured (SCG-CSO-RSC) source: Settings > Security :: Sign-in · Enterprise SSO / Enabled [DEVIATION] Enforce SSO for domain (SCG-CSO-RSC) source: Settings > Security :: Sign-in · Enterprise SSO / Enforce SSO recommended: True current: 'false' [OK ] Just-in-Time provisioning (SCG-CSO-RSC) source: Settings > Security :: Sign-in · Enterprise SSO / JIT provisioning [OK ] Google sign-in (SCG-CSO-RSC) source: Settings > Security :: Sign-in · Google / Enabled [N/A ] Enforce Google sign-in for domains (SCG-CSO-RSC) source: Settings > Security :: Sign-in · Google / Enforce for domains Google sign-in is not enabled [N/A ] Google JIT provisioning (SCG-CSO-RSC) source: Settings > Security :: Sign-in · Google / JIT provisioning Google sign-in is not enabled, so it provisions no accounts [OK ] SCIM 2.0 directory sync (SCG-CSO-RSC) source: Settings > Security :: Directory Sync (SCIM) / Configured [OK ] Default provisioned role (SCG-CSO-RSC) source: Settings > Security :: Identity Provisioning / Default provisioned role [OK ] Deploy key expiry (SCG-CSO-RSC) source: Settings > Security :: Deploy Keys / Configured keys + Deploy Key · / Expires all 2 key(s) have an expiry [OK ] Number of Owner accounts (SCG-CSO-RSC) source: Identities :: Role MANUAL REVIEW (no customer-reachable export today) [REVIEW ] Recommended built-in guardrails enabled (SCG-CSO-SDF) recommended: True check in: Guardrails [REVIEW ] Parameter Validation & Input Sanitization mode (SCG-CSO-SDF) recommended: 'block' check in: Guardrails [REVIEW ] Credential & Secret Protection mode (SCG-CSO-SDF) recommended: 'block' check in: Guardrails [REVIEW ] PII & Personal Data Protection mode (SCG-CSO-SDF) recommended: 'redact' check in: Guardrails [REVIEW ] In-Line Authorization & Destructive Action Blocking mode (SCG-CSO-SDF) recommended: 'block' check in: Guardrails [REVIEW ] Tool & Server Isolation mode (SCG-CSO-SDF) recommended: 'block' check in: Guardrails [REVIEW ] Rate Limiting mode (SCG-CSO-SDF) recommended: 'block' check in: Guardrails [REVIEW ] Anomaly detection (SCG-CSO-SDF) recommended: True check in: Identities [REVIEW ] Governance posture (SCG-CSO-SDF) recommended: 'default_deny' check in: Governance PLATFORM BEHAVIOR (not customer-configurable, informational) [INFO ] Guardrail fail behavior (SCG-CSO-SDF) = 'fail_closed' [INFO ] Open self-registration (SCG-CSO-SDF) = False [INFO ] Admin Log access (SCG-CSO-RSC) = 'owner_admin_only' [INFO ] Organization deletion (SCG-CSO-RSC) = 'owner_only' ------------------------------------------------------------------------------ 10 automated: 7 OK, 1 deviation(s), 0 no data, 2 n/a | 9 manual | 4 platform ``` **Reviewing deviations.** Deviations are reviewed by the organization's Owner or Admin, remediated in **Settings > Security**, and re-verified by re-running the comparison. Ultra recommends running the comparison after any administrative change and on whatever recurring cadence your continuous-monitoring program requires, and updating the recommended baseline when Ultra's recommended security defaults change. ```python theme={null} #!/usr/bin/env python3 """ ultra_scg_compare.py: Ultra Secure Configuration Guide settings comparison. Compares an Ultra Hub tenant's exported settings against the recommended secure defaults in the machine-readable SCG baseline (Ultra-SCG-baseline.json) and reports each recommendation as matching, deviating, or requiring manual review. Provided as-is, without warranty of any kind, as a convenience for authorization and continuous-monitoring workflows. It is a reporting aid: it does not change any Ultra configuration, and its output is not a compliance determination. Your exports contain sensitive configuration data. Treat the input files and this tool's output as controlled material and store them accordingly. USAGE python ultra_scg_compare.py --baseline Ultra-SCG-baseline.json \\ --security-settings security-settings.json \\ --identities identities.csv python ultra_scg_compare.py ... --json # machine-readable report to stdout Both export arguments are optional. Recommendations whose export is not supplied are reported as NO DATA rather than silently passing. HOW A RESULT IS DECIDED Every check reports whether it actually observed a value, separately from whether that value matched. OK requires an observation: a recommendation whose value could not be read is NO DATA, never a pass. A row that is absent, blank, duplicated with conflicting values, or in a format this tool cannot read is not an observation. A recommendation can also be N/A: not applicable to this tenant, established from a value that WAS observed (for example, Enterprise SSO's JIT setting when the export shows Enterprise SSO is not enabled). N/A is neither a pass nor a failure and does not affect the exit code. HOW A BASELINE ENTRY BINDS TO AN EXPORT For most entries, verification.field is the exact "Category / Setting" key this tool looks up. Two entries are checked by dedicated functions instead, because one exported row cannot answer them, and for those the field string is descriptive only: deploy_keys.expiry_required (reads the key count and every per-key expiry row) and rbac.owner_count (reads the Identities Role column, so field is a column name rather than a Category / Setting key). Editing either changes nothing but the printed source line. An entry carrying maxRole is not in that group: its field is still the exact lookup key, read before the ranking is applied, so renaming it turns a passing check into NO DATA. maxOwners and maxRole are the thresholds these checks compare against. WHERE THE EXPORTS COME FROM --security-settings Settings -> Security, Export (CSV or JSON). Supplies the sign-in, Enterprise SSO, Google, SCIM, and deploy-key rows. --identities Identities page, Export (CSV or JSON). Supplies the Role column, used to count Owner accounts. Export the pages unmodified, and export Identities with no filters applied: the export honors whatever filters are active on the page, and a filtered roster undercounts Owner accounts. Some recommended settings are not present in any customer-reachable export today (guardrail enablement and modes, governance posture). The baseline marks these "manual" and this tool reports them for administrator review instead of guessing. Platform behaviors that a customer cannot configure are marked "platform" and are reported for information only. Neither affects the exit code. EXIT CODES 0 every automated check matched 1 at least one automated check deviated, or could not be verified 2 the baseline or an export was missing, unreadable, or not in the expected format """ import argparse import csv import json import sys from datetime import datetime # Values that read as "on" / "enabled" and "off" / "disabled" in an Ultra export. # A value in neither set is not coerced to either one: equivalent() reports it as # unrecognized. The empty string is deliberately absent from both -- a blank cell # is missing data, not an observed "off" -- and is filtered out before comparison # by observe(). Extend these lists only with values confirmed in a real export. TRUTHY = {"true", "yes", "on", "enabled", "active", "configured", "1"} FALSEY = {"false", "no", "off", "disabled", "inactive", "not configured", "none", "never", "0"} # Baseline entry fields this tool reads unconditionally. REQUIRED_ITEM_FIELDS = ("key", "title", "rule", "recommendedValue", "verification") class InputError(Exception): """A problem with a file this tool was given, reported without a traceback.""" def norm(v): if isinstance(v, bool): return "true" if v else "false" if v is None: return "" return str(v).strip().lower() def parse_timestamp(value): """Parse an RFC 3339 timestamp as the export emits it, else return None.""" text = str(value).strip() if not text: return None if text.endswith("Z"): text = text[:-1] + "+00:00" try: return datetime.fromisoformat(text) except ValueError: return None def equivalent(recommended, current): """Compare a recommended value against an observed value. Returns (matches, reason). Callers must establish that `current` was observed before calling this; it decides match-or-not, never observed-or-not. A value in neither TRUTHY nor FALSEY returns (False, reason) rather than being read as "off", so a value this tool does not understand is reported. """ r, c = norm(recommended), norm(current) if isinstance(recommended, bool): if c in TRUTHY: return (recommended is True), None if c in FALSEY: return (recommended is False), None return False, f"unrecognized value {current!r}; not treated as a match" if r == c: return True, None return False, None def _normalize_row(path, row): if not isinstance(row, dict): raise InputError(f"{path}: expected each export row to be an object") return {(k or "").strip().lower(): ("" if v is None else str(v).strip()) for k, v in row.items()} def read_export(path): """Read a CSV or JSON export into a list of {column: value} dicts. encoding="utf-8-sig" so a file saved with a UTF-8 byte-order mark parses rather than carrying the mark into its first column name. """ try: if path.lower().endswith(".csv"): with open(path, newline="", encoding="utf-8-sig") as f: return [_normalize_row(path, r) for r in csv.DictReader(f)] with open(path, encoding="utf-8-sig") as f: data = json.load(f) except OSError as exc: raise InputError(f"{path}: {exc.strerror or exc}") from exc except json.JSONDecodeError as exc: raise InputError(f"{path}: not valid JSON ({exc})") from exc except UnicodeDecodeError as exc: raise InputError(f"{path}: not UTF-8 ({exc.reason}). Re-export rather than " "re-saving; the export's category names contain non-ASCII " "characters that a cp1252 or UTF-16 save will mangle.") from exc except csv.Error as exc: raise InputError(f"{path}: not readable as CSV ({exc})") from exc if isinstance(data, dict): for container in ("rows", "data", "items"): if isinstance(data.get(container), list): data = data[container] break if not isinstance(data, list): raise InputError(f"{path}: expected a list of export rows") return [_normalize_row(path, r) for r in data] def load_kv_export(path): """Load a Settings -> Security export as {"Category / Setting": [values]}. The export is a normalized (Category, Setting, Value) table in both CSV and JSON form, so both collapse to the same map. Every value for a key is kept rather than the last one winning: deploy-key names are not unique within an organization, so two same-named keys produce two rows under one key, and dropping one of them would hide the key it belonged to. """ out = {} for r in read_export(path): cat, setting = r.get("category", ""), r.get("setting", "") if not setting: continue out.setdefault(f"{cat} / {setting}".strip().lower(), []).append(r.get("value", "")) return out def load_table_export(path): """Load a row-per-entity export (for example the Identities export).""" return read_export(path) def observe(field, kv): """Read one field from the Settings -> Security export. Returns (observed, value, reason). observed is True only when the export carries exactly one non-blank value for the field. An absent row, a blank or whitespace-only value, and conflicting duplicate rows are all "not observed" and carry the reason they were not usable. """ key = field.strip().lower() values = kv.get(key) if not values: return False, None, f"row not present in the export: {field}" if len({norm(v) for v in values}) > 1: return False, None, f"conflicting rows for {field!r} in the export" value = values[0] if norm(value) == "": return False, None, f"blank value for {field!r}; nothing was observed" return True, value, None def not_applicable(item, kv): """Decide whether a recommendation is moot for this tenant. The condition names another field and the value that makes this recommendation not apply. It fires only when that field is ITSELF observed to hold that value, so a missing or unreadable row can never turn a check off. """ cond = item.get("verification", {}).get("notApplicableWhen") if not cond or kv is None: return False, "" observed, value, _ = observe(cond.get("field", ""), kv) if not observed: return False, "" matches, _ = equivalent(cond.get("equals"), value) if not matches: return False, "" return True, cond.get("reason", f"{cond.get('field')} is {norm(cond.get('equals'))}") def check_deploy_key_expiry(kv): """Check that every deploy key carries an expiry. "Deploy Keys / Configured keys" is the authority on how many keys exist, so zero keys reads as satisfied rather than unverifiable. The per-key clusters are cross-checked against that count in every case, including zero, so a count that disagrees with the rows present is reported rather than trusted. """ observed, declared, reason = observe("Deploy Keys / Configured keys", kv) if not observed: return "NO DATA", None, reason try: count = int(str(declared).strip()) except ValueError: return "NO DATA", None, f"unrecognized deploy key count {declared!r}" expiries = {k: v for k, v in kv.items() if k.startswith("deploy key ") and k.endswith("/ expires")} total_rows = sum(len(v) for v in expiries.values()) if len(expiries) != count: return ("NO DATA", f"{len(expiries)} expiry row(s) for {count} key(s)", "deploy key names are not unique within an organization, so same-named " "keys share one row; this export cannot be read key by key") if total_rows != count: return ("NO DATA", f"{total_rows} expiry value(s) for {count} key(s)", "duplicate rows for the same deploy key name; this export cannot be " "read key by key") if count == 0: return "OK", "0 deploy key(s)", "no long-lived proxy credentials configured" no_expiry, unreadable = [], [] for key, values in sorted(expiries.items()): label = key.rsplit(" / ", 1)[0] for value in values: if norm(value) == "never": no_expiry.append(label) elif parse_timestamp(value) is None: unreadable.append(f"{label}: {value!r}") if unreadable: return ("DEVIATION", f"{len(unreadable)} of {count} key(s) unreadable", "expiry is neither 'Never' nor an RFC 3339 timestamp, so it is not " "treated as an expiry: " + "; ".join(unreadable)) if no_expiry: return ("DEVIATION", f"{len(no_expiry)} of {count} key(s) have no expiry set", "an expiry of 'Never' means no expiry is set") return "OK", f"{count} deploy key(s)", f"all {count} key(s) have an expiry" # Provisioning roles, least privileged first. Ultra clamps the provisioning # default to this set and resolves anything else to member, so a value outside # it means the export is not the one this check reads. PROVISIONING_ROLE_RANK = ["beacon", "viewer", "member"] def check_role_ceiling(observed, ceiling): """Check a role-valued recommendation against a least-privilege ceiling. Ranks the observed role rather than matching one exact value, so a tenant hardened below the ceiling passes instead of being reported as a deviation. """ o, c = norm(observed), norm(ceiling) if o not in PROVISIONING_ROLE_RANK: # Ultra clamps this setting to the ranked set, so a value outside it is # not a role this check can place. Said plainly rather than as a bare # "unrecognized", since an over-privileged value is the case an auditor # cares about and it lands here rather than on DEVIATION. return "NO DATA", observed, ( f"role {observed!r} is outside the set Ultra clamps this setting to " f"({', '.join(PROVISIONING_ROLE_RANK)}); if it names a more privileged " "role, treat it as a deviation and check the export was not edited") if c not in PROVISIONING_ROLE_RANK: return "NO DATA", observed, f"baseline maxRole {ceiling!r} is not a known role" if PROVISIONING_ROLE_RANK.index(o) <= PROVISIONING_ROLE_RANK.index(c): return "OK", observed, ("" if o == c else f"tighter than the {c} ceiling") return "DEVIATION", observed, f"more privileged than the recommended ceiling {c!r}" def check_owner_count(rows, limit): """Count Owner accounts in the Identities export (Role column). Only role values this tool recognizes are counted. An unrecognized non-blank role means the column is not the one this check was written against, so the count would be wrong: that is reported rather than counted around. An organization always has at least one Owner, so a count of zero likewise means the column was not exported as-is. """ known = {"owner", "admin", "member", "viewer", "beacon", ""} if not rows: return "NO DATA", None, "the Identities export contains no rows" if not any("role" in r for r in rows): return "NO DATA", None, ("no 'Role' column in the Identities export; " "export the Identities page unmodified") unknown = sorted({norm(r.get("role")) for r in rows} - known) if unknown: return ("NO DATA", None, "unrecognized role value(s) " + ", ".join(repr(u) for u in unknown) + "; the Role column is not the one this check reads, so the count " "would be wrong") owners = sum(1 for r in rows if norm(r.get("role")) == "owner") if owners == 0: return "NO DATA", None, ("no Owner accounts found, which cannot happen in a real " "organization; check the Role column was exported " "unmodified and unfiltered") return (("OK" if owners <= limit else "DEVIATION"), f"{owners} Owner account(s)", "" if owners <= limit else f"recommended no more than {limit}") def evaluate(item, security_kv, identity_rows): """Evaluate one baseline entry. Returns (status, current, detail).""" v = item["verification"] method = v.get("method") if method == "manual": return "MANUAL", None, v.get("source", "") if method == "platform": return "PLATFORM", item["recommendedValue"], v.get("source", "") if method == "export" and v.get("export") == "security-settings": if security_kv is None: return "NO DATA", None, "Settings -> Security export not supplied" skip, why = not_applicable(item, security_kv) if skip: return "N/A", None, why if item["key"] == "deploy_keys.expiry_required": return check_deploy_key_expiry(security_kv) observed, current, reason = observe(v.get("field", ""), security_kv) if not observed: return "NO DATA", None, reason if "maxRole" in item: return check_role_ceiling(current, item["maxRole"]) ok, why_not = equivalent(item["recommendedValue"], current) return ("OK" if ok else "DEVIATION"), current, (why_not or "") if method == "export" and v.get("export") == "identities": if identity_rows is None: return "NO DATA", None, "Identities export not supplied" return check_owner_count(identity_rows, item.get("maxOwners", 3)) return "NO DATA", None, "no verification method defined" def load_baseline(path): try: with open(path, encoding="utf-8-sig") as f: baseline = json.load(f) except OSError as exc: raise InputError(f"{path}: {exc.strerror or exc}") from exc except json.JSONDecodeError as exc: raise InputError(f"{path}: not valid JSON ({exc})") from exc except UnicodeDecodeError as exc: raise InputError(f"{path}: not UTF-8 ({exc.reason}). Copy the baseline from " "the guide rather than re-saving it.") from exc items = baseline.get("recommendedSecureDefaults") if isinstance(baseline, dict) else None if not isinstance(items, list) or not items: raise InputError(f"{path}: no 'recommendedSecureDefaults' list; this does not " "look like an Ultra SCG baseline") for i, item in enumerate(items): if not isinstance(item, dict): raise InputError(f"{path}: recommendedSecureDefaults[{i}] is not an object") missing = [f for f in REQUIRED_ITEM_FIELDS if f not in item] if missing: raise InputError(f"{path}: recommendedSecureDefaults[{i}] " f"({item.get('key', 'no key')}) is missing " + ", ".join(missing)) if not isinstance(item["verification"], dict): raise InputError(f"{path}: recommendedSecureDefaults[{i}] " f"({item['key']}) has a non-object 'verification'") return items def main(): ap = argparse.ArgumentParser( description="Compare Ultra Hub settings against the SCG baseline.") ap.add_argument("--baseline", required=True, help="Ultra-SCG-baseline.json") ap.add_argument("--security-settings", help="Settings -> Security export (.json or .csv)") ap.add_argument("--identities", help="Identities export (.json or .csv)") ap.add_argument("--json", action="store_true", help="emit machine-readable JSON report") args = ap.parse_args() try: items = load_baseline(args.baseline) security_kv = load_kv_export(args.security_settings) if args.security_settings else None identity_rows = load_table_export(args.identities) if args.identities else None except InputError as exc: print(f"error: {exc}", file=sys.stderr) return 2 rows = [] for item in items: status, current, detail = evaluate(item, security_kv, identity_rows) rows.append({ "key": item["key"], "title": item["title"], "rule": item["rule"], "recommended": item["recommendedValue"], "current": current, "source": item["verification"].get("source", ""), "field": item["verification"].get("field", ""), "detail": detail, "status": status, }) automated = [r for r in rows if r["status"] in ("OK", "DEVIATION", "NO DATA", "N/A")] manual = [r for r in rows if r["status"] == "MANUAL"] platform = [r for r in rows if r["status"] == "PLATFORM"] ok = sum(1 for r in automated if r["status"] == "OK") dev = sum(1 for r in automated if r["status"] == "DEVIATION") nodata = sum(1 for r in automated if r["status"] == "NO DATA") na = sum(1 for r in automated if r["status"] == "N/A") if args.json: print(json.dumps({ "summary": {"automated": len(automated), "ok": ok, "deviations": dev, "noData": nodata, "notApplicable": na, "manual": len(manual), "platform": len(platform)}, "results": rows}, indent=2, default=str)) else: print("\nUltra SCG settings comparison") print("=" * 78) print("\nAUTOMATED (verified from an export)") for r in automated: print(f" [{r['status']:9}] {r['title']} ({r['rule']})") print(f" source: {r['source']} :: {r['field']}") if r["status"] == "DEVIATION": print(f" recommended: {r['recommended']!r} current: {r['current']!r}") if r["detail"]: print(f" {r['detail']}") print("\nMANUAL REVIEW (no customer-reachable export today)") for r in manual: print(f" [REVIEW ] {r['title']} ({r['rule']})") print(f" recommended: {r['recommended']!r} check in: {r['source']}") print("\nPLATFORM BEHAVIOR (not customer-configurable, informational)") for r in platform: print(f" [INFO ] {r['title']} ({r['rule']}) = {r['recommended']!r}") print("-" * 78) print(f"{len(automated)} automated: {ok} OK, {dev} deviation(s), {nodata} no data, " f"{na} n/a | {len(manual)} manual | {len(platform)} platform") return 1 if (dev or nodata) else 0 if __name__ == "__main__": sys.exit(main()) ``` ## Versioning & release history This guide and the recommended secure default settings it documents are versioned. It is reviewed at least annually and upon any significant security change. Prior versions of the guide and the machine-readable baseline are retained by Ultra and available on request. | Version | Effective date | Author | Review cycle | Summary of changes | | - | - | - | - | - | | 1.7 | 2026-09-25 | Rob Gutierrez, Director, Security & Business Operations | Annual, or upon any significant security change | Recorded that the recommended built-in guardrails and anomaly detection are now enabled by default on a newly provisioned organization, reversing the prior "not active until enabled" guidance and adding an anomaly-detection baseline entry (manual verification). Aligned the cloud service offering name to "Ultra Agentic Security Platform", standardized the enforcement component name to "Ultra Proxy", and centered the guide on the single platform. | | 1.6 | 2026-08-31 | Rob Gutierrez, Director, Security & Business Operations | Annual, or upon any significant security change | Restated guardrail and governance scope resolution to match the platform's composition model, in which a narrower scope may add restrictions but never remove them: the strictest enabled guardrail row wins with scope only as a tie-break, and the effective governance posture is the stricter of the organization and workspace settings, replacing the previous claim that a workspace posture overrides the organization's. Recorded that governance rules are evaluated as a set rather than resolved to one winner, and that Rate Limiting is enforced on self-hosted devices only. Removed `alert` from the enforcement modes named in the fail-behavior limits, since it is no longer a mode. Resolved the remaining `maxRole` contradiction, in which the lead-in and entry table still grouped a `maxRole` entry with the descriptive-field entries, contradicting the corrected paragraph v1.5 added below them; corrected the API-capability table's directory-sync row, which still described the endpoint as returning a status; and extended the `NO DATA` definition to cover a value that is observed but cannot be ranked. | | 1.5 | 2026-08-19 | Rob Gutierrez, Director, Security & Business Operations | Annual, or upon any significant security change | Corrected the settings table's default-provisioned-role row to match the baseline in name, scope and ceiling; made the Google recommendation and the table agree; corrected the claim that a `maxRole` entry's field is descriptive, since it is the lookup key; stated what the directory-sync endpoint returns to a Viewer; extended encoding-error handling to the baseline loader; widened the guardrail fail-behavior lead-in from two limits to four; qualified the Admin Log's member-role event as team-scoped on the Admin Log page; and reworded the role-ceiling message to name the over-privileged case. | | 1.4 | 2026-08-19 | Rob Gutierrez, Director, Security & Business Operations | Annual, or upon any significant security change | Rebound the provisioning default role to the row the export actually emits and ranked it against a least-privilege ceiling instead of one exact value, restoring a reachable exit 0; extended the recommended built-in guardrail set to all six the catalog recommends; added baseline entries for Google sign-in and organization deletion; made undecodable and malformed exports exit 2 rather than 1; corrected Member device verbs and the Viewer row, replaced the Beacon justification with a credential-class argument, documented the organization-level role cascade and the device-owner escalation path, narrowed the Admin Log coverage claim to what emits today, and widened the guardrail fail-behavior limits. | | 1.3 | 2026-08-18 | Rob Gutierrez, Director, Security & Business Operations | Annual, or upon any significant security change | Rebuilt the comparison utility around an explicit observed-or-not decision, so no recommendation can pass on data that was never read; replaced `absentMeans` with `notApplicableWhen` and documented it; extended the baseline to Google sign-in enforcement and JIT and to the SCIM default role; corrected the deploy-key revocation mechanism, the Viewer and Member privilege rows, and the description of which credentials use `Authorization: Bearer`; adopted the connectors / devices / agents product taxonomy throughout, retaining the shipped guardrail name Tool & Server Isolation and the stable wire names used by the Hub API and its exports. | | 1.2 | 2026-08-13 | Rob Gutierrez, Director, Security & Business Operations | Annual, or upon any significant security change | Corrected administrative API authentication to cover session, OAuth2, and API-token credentials, and split endpoint permissions into reads and writes; corrected Admin Log access to view-only and restated Member workspace and device privileges; corrected the assurance-tier labels to the shipped names and removed the claim that revocation prevents a device restart; split the guardrail recommendation into per-guardrail modes; closed the remaining fail-open paths in the comparison utility and made a hardened tenant verifiable end to end. | | 1.1 | 2026-08-10 | Rob Gutierrez, Director, Security & Business Operations | Annual, or upon any significant security change | Corrected guardrail activation, scope precedence, and role capabilities against product behavior; bound the machine-readable baseline to real Hub exports and added verification methods; documented known audit-coverage gaps. | | 1.0 | 2026-08-09 | Rob Gutierrez, Director, Security & Business Operations | Annual, or upon any significant security change | Initial publication supporting FedRAMP 20x Class B. | # Configuration File Source: https://docs.ultra.security/configuration/config-file Ultra's YAML configuration format and options Ultra uses a YAML configuration file to define upstream connectors, observability settings, and Hub connection details. ## Config Search Order Ultra searches for configuration files in this order (first match wins): 1. `ultra.yaml` or `ultra.yml` (current directory) 2. `.ultra.yaml` or `.ultra.yml` (current directory) 3. `$XDG_CONFIG_HOME/ultra/config.yaml` (default: `~/.config/ultra/config.yaml`) 4. `$XDG_CONFIG_HOME/ultra/config.yml` 5. `~/.ultra.yaml` or `~/.ultra.yml` If no config file is found, Ultra uses sensible defaults. ## Show Config Path ```bash theme={null} ultra config path ``` ## Default Config Path ``` ~/.config/ultra/config.yaml ``` ## Full Example ```yaml theme={null} version: "1" server: name: ultra log_level: info # debug, info, warn, error upstream: filesystem: command: npx args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"] enabled: true description: "Local filesystem access" github: command: npx args: ["-y", "@modelcontextprotocol/server-github"] env: GITHUB_TOKEN: "ghp_xxxxxxxxxxxx" enabled: true notion: url: "https://mcp.notion.so" headers: Authorization: "Bearer secret_xxxxxxxxxxxx" enabled: true observability: enabled: false # Set to true for OTLP export endpoint: "" # e.g., "localhost:4317" service_name: ultra hub: enabled: false url: "https://hub.ultra.security" sync_interval: "60s" offline_mode: false ``` ## Configuration Reference ### `server` | Field | Type | Default | Description | | - | - | - | - | | `name` | string | `"ultra"` | Server name advertised to MCP agents | | `log_level` | string | `"info"` | Logging level: `debug`, `info`, `warn`, `error` | ### `upstream` A map of named upstream MCP connectors. Each connector uses either **stdio** transport (command) or **HTTP** transport (url). | Field | Type | Required | Description | | - | - | - | - | | `command` | string | For stdio | Command to execute | | `args` | string\[] | No | Command arguments | | `env` | map | No | Environment variables | | `url` | string | For HTTP | HTTP URL for remote connectors | | `headers` | map | No | HTTP headers (e.g., auth tokens) | | `enabled` | bool | No | Whether this connector is active (default: false) | | `description` | string | No | Human-readable description | Each upstream must specify either `command` or `url`, not both. ### `observability` | Field | Type | Default | Description | | - | - | - | - | | `enabled` | bool | `false` | Enable OTLP telemetry export | | `endpoint` | string | `""` | OTLP endpoint (e.g., `localhost:4317`) | | `service_name` | string | `"ultra"` | Service name for telemetry | ### `hub` | Field | Type | Default | Description | | - | - | - | - | | `enabled` | bool | `false` | Enable Hub sync | | `url` | string | `"https://hub.ultra.security"` | Hub API URL | | `gateway_id` | string | | Assigned by Hub during linking | | `sync_interval` | string | `"60s"` | Sync frequency (e.g., `"60s"`, `"5m"`) | | `offline_mode` | bool | `false` | Allow operation without Hub connectivity | | `deploy_key` | string | | Static API key for CI/headless deployments | Hub authentication tokens (`access_token`, `refresh_token`, `token_expiry`) are managed automatically by `ultra login` and should not be edited manually. ### Policy enforcement Policy enforcement is not configured in this file. [Guardrails](/hub/guardrails) and [governance rules](/hub/governance) are configured in Ultra Hub and synced automatically to every connected device, so enforcement stays consistent across your fleet without editing local config. ## File Permissions The config file is saved with `0600` permissions (owner read/write only) because it may contain authentication tokens and API keys. ## Programmatic Config Management Use `ultra config` subcommands to modify configuration: ```bash theme={null} # Set an auth token for a connector ultra config set-token github-mcp-server YOUR_TOKEN # Set a custom header ultra config set-header my-connector X-API-Key YOUR_KEY # Show config file path ultra config path ``` # Hub Settings Source: https://docs.ultra.security/configuration/hub-settings Configure Ultra Hub connection settings The `hub` section of your Ultra config controls how this device connects to Ultra Hub for centralized management, telemetry sync, and team collaboration. ## Configuration Reference ```yaml theme={null} hub: enabled: false url: "https://hub.ultra.security" gateway_id: "" # Assigned by Hub during linking workspace_id: "" # Workspace this device is linked to sync_interval: "60s" # How often to sync (e.g., "60s", "5m") offline_mode: false # Allow operation without Hub connectivity deploy_key: "" # Static API key for CI/headless deployments ``` | Field | Type | Default | Description | | - | - | - | - | | `enabled` | bool | `false` | Enable Hub connectivity | | `url` | string | `https://hub.ultra.security` | Hub API URL | | `gateway_id` | string | | This device's unique ID (set by `ultra link`) | | `workspace_id` | string | | Linked workspace ID (set by `ultra link`) | | `sync_interval` | string | `"60s"` | How often to sync traces/events to Hub | | `offline_mode` | bool | `false` | Continue operating if Hub is unreachable | | `deploy_key` | string | | Static API key for headless/CI deployments | ## Setting Up Hub Connection You can set up Hub connection by asking your agent: 1. Create an account at [hub.ultra.security](https://hub.ultra.security) and set up a workspace 2. Ask your agent to connect: * *"Log in to Ultra Hub"* * *"Link this device to Ultra Hub"* 3. **Restart your agent** for the connection to take effect Once linked, Ultra automatically syncs your traces and audit events to Hub in the background. You can manage your device, invite team members, and view all MCP activity from the Hub dashboard. Hub connection is typically configured through CLI commands rather than editing the config directly: ```bash theme={null} # 1. Authenticate with Hub ultra login # 2. Link this device to a workspace ultra link # 3. Start Ultra — it will sync automatically ultra start ``` These commands set `enabled`, `url`, `gateway_id`, `workspace_id`, and authentication tokens in your config. ## Authentication Methods Ultra Hub supports two authentication methods: ### OAuth2 (Interactive) For developer workstations. Run `ultra login` to authenticate via browser: ```bash theme={null} ultra login ``` Tokens are stored in the config and refreshed automatically. If your session expires, Ultra prints a single warning and disables sync until you re-authenticate. ### Deploy Keys (Headless/CI) For CI/CD pipelines and servers without browser access: ```bash theme={null} # Set via environment variable export ULTRA_DEPLOY_KEY=dk_xxxxxxxxxxxx # Or in config hub: deploy_key: "dk_xxxxxxxxxxxx" ``` Deploy keys start with the `dk_` prefix. The environment variable `ULTRA_DEPLOY_KEY` takes priority over the config value. ## Sync Behavior When Hub is enabled and the device is linked, `ultra start` runs a background sync loop: * **Traces and audit events** are synced to Hub at the configured interval * **Heartbeats** are sent at half the sync interval (minimum 30 seconds) * **Token refresh** happens automatically before sync operations * If authentication fails permanently (e.g., revoked token), sync is disabled with a single warning message ## Self-Hosted Hub To connect to a self-hosted Hub instance: ```bash theme={null} ultra login --hub https://hub.mycompany.com ``` Or set in config: ```yaml theme={null} hub: url: "https://hub.mycompany.com" ``` ## Disconnecting ```bash theme={null} # Unlink device (keeps auth tokens for future linking) ultra unlink # Full logout (clears all Hub credentials) ultra logout ``` # Upstream Connectors Source: https://docs.ultra.security/configuration/upstream-connectors Configure and manage MCP connectors Ultra proxies requests to one or more upstream MCP connectors. Connectors can use **stdio** transport (local commands) or **HTTP** transport (remote URLs). Only add MCP connectors from official or trusted sources. Untrusted community connectors can execute arbitrary code on your machine (stdio transport) or receive sensitive data from your AI agents (HTTP transport). Stick to connectors from the [official MCP Registry](https://github.com/modelcontextprotocol/servers), verified publishers, or connectors your organization has reviewed and approved. ## Add Connectors Through Your Agent The simplest way to add MCP connectors is to ask your agent to do it for you. Since Ultra is already connected to your agent, just tell it what services you want to connect: * *"Connect to the Notion connector"* * *"Add the GitHub connector"* * *"Set up the filesystem connector so you can read my project files"* * *"Add the Linear connector"* Your agent will work with Ultra to search the registry, install the connector, and configure any required authentication (API keys, OAuth, etc.). In agents that support MCP Apps, you can view, discover, add, and reconnect connectors from an interactive panel — see the [ultra\_servers MCP App](/getting-started/mcp-app). Ultra hot-loads the new connector with no restart of its own. Most agents pick up the new tools automatically; if they don't appear, refresh or restart your agent. Make sure your agent installs the **official** MCP connector for each service — not a random community fork. Look for connectors published by the service provider (e.g., `@notionhq/notion-mcp-server`) or from the [official MCP Registry](https://github.com/modelcontextprotocol/servers). If you're unsure, ask your agent to confirm the publisher before installing. You can also ask your agent to list your current connectors (*"What connectors do I have connected?"*) or remove ones you no longer need. ## Popular Connectors Select a connector to see setup instructions. Ask your agent: *"Add the Notion connector"* Notion uses **OAuth**. You will be redirected to Notion in your browser to authorize access to your workspace. Tokens are stored and refreshed automatically. You need to be a member of the Notion workspace you want to connect. Slack needs a Slack app in your workspace before Ultra can connect. Slack's MCP server does not register clients automatically, so the OAuth flow needs a pre-registered app's client ID and secret. 1. Create or open an app at [api.slack.com/apps](https://api.slack.com/apps). Slack allows only internal or directory-published apps to use MCP. 2. Under **OAuth & Permissions**, grant the **user token scopes** in the table below, then install the app to your workspace. 3. Copy the **Client ID** and **Client Secret** from **Basic Information > App Credentials**. 4. Ask your agent: *"Add the Slack connector"* 5. Ask your agent to set the credentials, for example *"Set the Slack connector's client\_id and client\_secret"* followed by the two values. Your agent applies them with the [`ultra_configure`](/getting-started/ultra-mcp-tools#ultra_configure) tool. 6. Ask your agent to reconnect Slack. Authorize in your browser when Slack prompts you. ### Required scopes Ultra currently requests every Slack MCP user-token scope below. Add the same scopes to the Slack app before reconnecting; otherwise Slack may reject the request or issue a token that cannot use every Slack MCP tool. | Capability | User token scopes | | - | - | | Search your workspace | `search:read.public`, `search:read.private`, `search:read.mpim`, `search:read.im`, `search:read.files`, `search:read.users` | | Read conversation history | `channels:history`, `groups:history`, `im:history`, `mpim:history` | | Send messages and react | `chat:write`, `channels:write`, `groups:write`, `im:write`, `mpim:write`, `reactions:write` | | Look up channels and people | `channels:read`, `groups:read`, `mpim:read`, `users:read`, `users:read.email` | | Read files and emoji | `files:read`, `emoji:read` | | Work with canvases | `canvases:read`, `canvases:write` | Ask your agent: *"Add the Linear connector"* Linear uses **OAuth**. Authorize in your browser when prompted. Ask your agent: *"Add the GitHub connector"* You will be prompted for a **Personal Access Token (PAT)**. Create one at [github.com/settings/tokens](https://github.com/settings/tokens) with the scopes you need (typically `repo` and `read:org`). Use a **fine-grained PAT** scoped to only the repositories you need for better security. Ask your agent: *"Add the Figma connector"* Figma uses **OAuth**. You will be redirected to Figma in your browser to authorize access to your files and projects. Ask your agent: *"Add the AWS connector"* You will need AWS credentials configured on your machine (via `~/.aws/credentials` or environment variables). Ask your agent: *"Add the Jira connector"* You will be prompted for your Jira instance URL and an **API token**. Create one at [id.atlassian.com/manage-profile/security/api-tokens](https://id.atlassian.com/manage-profile/security/api-tokens). Ask your agent: *"Add the Mintlify connector"* You will need your Mintlify project subdomain or API key. Ask your agent: *"Add the Salesforce connector"* Salesforce uses **OAuth**. You will be redirected to Salesforce in your browser to authorize access to your org. Ask your agent: *"Add the Datadog connector"* You will be prompted for your Datadog **API key** and **application key**. Find these in your Datadog org settings under API Keys. Ask your agent: *"Add the PostgreSQL connector"* You will be prompted for your database connection string. Use a read-only database user when possible. Ask your agent: *"Add the Vanta connector"* Vanta uses **OAuth**. You will be redirected to Vanta in your browser to authorize. Once approved, the token is stored and refreshed automatically. You need an active Vanta account with appropriate permissions. If you see an authentication error, check with your Vanta admin that your account has API access enabled. Ask your agent: *"Add the filesystem connector for my project files"* The filesystem connector runs locally and does not require authentication. You will be asked which directories to allow access to. Only grant access to directories you are comfortable sharing with your AI agent. Never point it at your home directory or root. ## Supported Agents Ultra works with all major MCP-compatible AI agents: | Agent | Type | | - | - | | **Claude Desktop** | Desktop app | | **Cursor** | IDE | | **VS Code** | IDE | | **GitHub Copilot** | IDE (via VS Code) | | **Claude Code** | CLI | | **Codex** | CLI | | **Windsurf** | IDE | | **Roo Code** | VS Code extension | | **Goose** | CLI | | **Gemini** | Desktop app | | **Antigravity** | IDE / CLI | | **LM Studio** | Desktop app | | **Amazon Q Developer** | CLI | | **Zed** | IDE | | **Ona** | Cloud dev environment | | **Hermes** | Agent framework | Run `ultra doctor` to see which agents are detected on your machine, or `ultra install` to configure a specific agent. ## Adding Connectors from Registry The easiest way to add connectors is from the MCP registry: ```bash theme={null} # Search for connectors ultra search filesystem # Add by name (searches registries) ultra add filesystem # Add by full package name ultra add @modelcontextprotocol/server-filesystem # Add with a custom name ultra add @modelcontextprotocol/server-filesystem --name myfiles ``` When adding from the registry, Ultra automatically tests the connection, detects authentication requirements, and prompts for any required configuration (API keys, file paths, etc.). ## Adding Remote Connectors by URL Add HTTP-based MCP connectors directly by URL: ```bash theme={null} # Add a remote connector ultra add https://mcp.example.com/mcp # With a custom name ultra add https://remote.example.com/mcp --name myconnector ``` Ultra auto-detects transport type and authentication requirements. If the connector requires OAuth, you'll be prompted to authorize when it's first used. For token-based auth, Ultra prompts for the token during setup. Connectors that don't register OAuth clients automatically, such as Slack, need a pre-registered app's `client_id` and `client_secret` on the entry as well. See the connector's own instructions below. ## Popular Connectors Select a connector below for specific setup instructions. ```bash theme={null} ultra add notion ``` Notion uses **OAuth**. Ultra opens your browser for authorization. Tokens are stored and refreshed automatically. You need to be a member of the Notion workspace you want to connect. ```bash theme={null} ultra add https://mcp.slack.com/mcp --name slack ``` Slack's MCP server does not register clients automatically, so adding the URL is not enough on its own. Create or open an app at [api.slack.com/apps](https://api.slack.com/apps), grant it the user token scopes listed below, then copy the **Client ID** and **Client Secret** from **Basic Information > App Credentials**. There is no CLI flag for these values. The quickest way to apply them is to ask your agent to set the Slack connector's `client_id` and `client_secret`, which it does with the [`ultra_configure`](/getting-started/ultra-mcp-tools#ultra_configure) tool. To set them by hand instead, add them to the `slack` entry in your config file (`ultra config path` prints its location): ```yaml theme={null} upstream: slack: url: https://mcp.slack.com/mcp client_id: 1234567890.9876543210 client_secret: abc123def456 enabled: true ``` Paste the raw values; quotes are not required. Either way, reconnect Slack afterward with `ultra reconnect slack`. Ultra opens your browser to authorize, and once approved your agent can send messages, search conversations, and interact with your workspace. ### Required scopes Ultra currently requests every Slack MCP user-token scope below. Add the same scopes to the Slack app before reconnecting; otherwise Slack may reject the request or issue a token that cannot use every Slack MCP tool. | Capability | User token scopes | | - | - | | Search your workspace | `search:read.public`, `search:read.private`, `search:read.mpim`, `search:read.im`, `search:read.files`, `search:read.users` | | Read conversation history | `channels:history`, `groups:history`, `im:history`, `mpim:history` | | Send messages and react | `chat:write`, `channels:write`, `groups:write`, `im:write`, `mpim:write`, `reactions:write` | | Look up channels and people | `channels:read`, `groups:read`, `mpim:read`, `users:read`, `users:read.email` | | Read files and emoji | `files:read`, `emoji:read` | | Work with canvases | `canvases:read`, `canvases:write` | Slack allows only internal or directory-published apps to use MCP. ```bash theme={null} ultra add linear ``` Linear uses **OAuth**. Authorize in your browser when prompted. Your agent can then read and manage issues, projects, and cycles. ```bash theme={null} ultra add github ``` GitHub requires a **Personal Access Token (PAT)**. Ultra prompts you to enter it during setup. Create one at [github.com/settings/tokens](https://github.com/settings/tokens) with the scopes you need (typically `repo` and `read:org`). Use a **fine-grained PAT** scoped to only the repositories you need for better security. ```bash theme={null} ultra add https://mcp.figma.com/mcp --name figma ``` Figma uses **OAuth**. Ultra opens your browser for authorization. Tokens are stored and refreshed automatically. ```bash theme={null} ultra add aws ``` The AWS connector uses your local AWS credentials (`~/.aws/credentials` or environment variables). Make sure `aws configure` is set up before adding. ```bash theme={null} ultra add jira ``` Jira requires your instance URL and an **API token**. Create one at [id.atlassian.com/manage-profile/security/api-tokens](https://id.atlassian.com/manage-profile/security/api-tokens). Ultra prompts for both during setup. ```bash theme={null} ultra add mintlify ``` When prompted, enter your Mintlify project subdomain or API key. ```bash theme={null} ultra add salesforce ``` Salesforce uses **OAuth**. Ultra opens your browser for authorization. Tokens are stored and refreshed automatically. ```bash theme={null} ultra add datadog ``` Datadog requires an **API key** and **application key**. Find these in your Datadog org settings under API Keys. Ultra prompts for both during setup. ```bash theme={null} ultra add postgres ``` When prompted, enter your PostgreSQL connection string (e.g., `postgresql://user:pass@host:5432/dbname`). Use a read-only database user when possible. ```bash theme={null} ultra add https://mcp.vanta.com/mcp --name vanta ``` Vanta uses **OAuth**. Ultra detects this automatically and opens your browser to authorize. Once approved, tokens are stored and refreshed automatically. You need an active Vanta account with appropriate permissions. If you see an authentication error, check with your Vanta admin that your account has API access enabled. ```bash theme={null} ultra add filesystem ``` The filesystem connector runs locally (stdio transport) and does not require authentication. When prompted, specify the directory paths you want to allow access to. Only grant access to directories you are comfortable sharing with your AI agent. Never point it at your home directory or root. ## Adding Connectors Manually Use `ultra connectors add` for full control: ```bash theme={null} # Stdio transport (local command) ultra connectors add filesystem \ --command npx \ --args "-y @modelcontextprotocol/server-filesystem /tmp" # HTTP transport (remote URL) ultra connectors add remote-api --url https://mcp.example.com/sse # With environment variables ultra connectors add github \ --command npx \ --args "-y @modelcontextprotocol/server-github" \ --env "GITHUB_TOKEN=ghp_xxx" # Add in disabled state ultra connectors add test-connector --command ./test-server --disabled ``` ## Managing Connectors ```bash theme={null} # List all connectors ultra connectors # Enable/disable ultra connectors enable filesystem ultra connectors disable filesystem # Remove ultra connectors remove filesystem ultra connectors remove filesystem --force # Skip confirmation ``` ## Stdio vs HTTP Transport ### Stdio Transport For connectors that run as local processes. Ultra spawns the process and communicates over stdin/stdout. ```yaml theme={null} upstream: filesystem: command: npx args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"] env: NODE_ENV: production enabled: true ``` ### HTTP Transport For remote connectors. Ultra connects over HTTP/SSE. ```yaml theme={null} upstream: notion: url: "https://mcp.notion.so" headers: Authorization: "Bearer secret_xxx" enabled: true ``` ## Authentication ### Token-Based Auth Set auth tokens for HTTP connectors: ```bash theme={null} # Set during add (interactive) ultra add https://mcp.example.com/sse # Set after adding ultra config set-token my-connector YOUR_TOKEN # Set a custom header ultra config set-header my-connector X-API-Key YOUR_KEY ``` Tokens are stored as `Authorization: Bearer {token}` headers in the config file. ### OAuth2 Ultra supports OAuth2 authentication for upstream connectors that require it. The OAuth flow is handled automatically at runtime — when a connector requires authorization, Ultra opens a browser for you to complete the flow and stores the tokens for future requests. ### Environment Variables For stdio connectors, pass secrets via environment variables instead of hardcoding them in args: ```yaml theme={null} upstream: github: command: npx args: ["-y", "@modelcontextprotocol/server-github"] env: GITHUB_TOKEN: "ghp_xxxxxxxxxxxx" enabled: true ``` # Quick Start for Users Source: https://docs.ultra.security/getting-started/end-user-quickstart A quick guide for getting started with Ultra Your admin has set up [Ultra](https://ultra.security) for your team. Ultra is a lightweight proxy that sits between your AI agent (Claude Desktop, Cursor, VS Code, etc.) and the MCP connectors you use every day. It gives your security team visibility into MCP activity without changing how you work. This guide gets you up and running in under 5 minutes. ## Install Ultra Sign in to [Ultra Hub](https://hub.ultra.security) with the invite from your admin, then download the installer for your platform from **Settings > Downloads**. Run the installer and it will automatically detect your AI agents and configure them. ```bash theme={null} curl -sSL https://get.ultra.security/install.sh | sh ``` Then configure your AI agent: ```bash theme={null} ultra install ``` **Restart your AI agent** (Claude Desktop, Cursor, etc.) after installing for Ultra to take effect. ## Connect to Your Team Your admin will provide you with a link to join your organization's Ultra Hub workspace. Once you receive the invite: Ask your AI agent: * *"Log in to Ultra Hub"* * *"Link this device to Ultra Hub"* Follow the browser prompts to authenticate and join your workspace. ```bash theme={null} ultra login ultra link ``` Once linked, your MCP activity syncs to [Ultra Hub](https://hub.ultra.security) where your admin can see connected devices, manage connectors, and monitor activity across the team. ## Add Your MCP Connectors This is the part that replaces your old MCP setup. Instead of editing JSON config files manually, you add connectors through Ultra and it handles configuration, authentication, and transport automatically. Ask your AI agent to add the connectors you need: * *"Add the Vanta MCP connector"* * *"Connect to the GitHub MCP connector"* * *"Set up the Notion MCP connector"* * *"Add the Slack MCP connector"* Your agent will search the registry, install the connector, and walk you through authentication. **Restart your AI agent** after adding connectors. ```bash theme={null} # Search for a connector ultra search vanta # Add it ultra add vanta ``` Ultra will search the registry, test the connection, and prompt for any required authentication (API keys, OAuth, etc.). **Restart your AI agent** after adding connectors. ### Popular Connector Setup Select a connector to see setup instructions. Connects your AI agent to your Notion workspace for reading and writing pages, databases, and comments. Ask your AI agent: *"Add the Notion MCP connector"* Notion uses OAuth. You will be redirected to Notion in your browser to authorize access to your workspace. ```bash theme={null} ultra add notion ``` Ultra detects Notion's OAuth flow and opens your browser for authorization. Tokens are stored and refreshed automatically. You need to be a member of the Notion workspace you want to connect. The OAuth consent screen will show which workspace pages the integration can access. Lets your AI agent send messages, search conversations, and interact with your Slack workspace. Ask your AI agent: *"Add the Slack MCP connector"* Slack uses OAuth. You will be redirected to Slack in your browser to authorize. ```bash theme={null} ultra add https://mcp.slack.com/mcp --name slack ``` Ultra handles the OAuth flow automatically. Authorize in your browser when prompted. Connects your AI agent to Linear for reading and managing issues, projects, and cycles. Ask your AI agent: *"Add the Linear MCP connector"* Linear uses OAuth. Authorize in your browser when prompted. ```bash theme={null} ultra add linear ``` Ultra detects Linear's OAuth flow and opens your browser for authorization. Gives your AI agent access to repositories, issues, pull requests, and code search. Ask your AI agent: *"Add the GitHub MCP connector"* You will be prompted for a **Personal Access Token (PAT)**. Create one at [github.com/settings/tokens](https://github.com/settings/tokens) with the scopes you need (typically `repo` and `read:org`). ```bash theme={null} ultra add github ``` When prompted, enter your GitHub PAT. The token is stored securely in your Ultra config. Use a **fine-grained PAT** scoped to only the repositories you need. This follows the principle of least privilege and is safer than a classic token with broad access. Connects your AI agent to Figma for accessing design files, components, and projects. Ask your AI agent: *"Add the Figma MCP connector"* Figma uses OAuth. You will be redirected to Figma in your browser to authorize access. ```bash theme={null} ultra add https://mcp.figma.com/mcp --name figma ``` Ultra handles the OAuth flow automatically. Authorize in your browser when prompted. Gives your AI agent access to AWS services, documentation, and infrastructure management. Ask your AI agent: *"Add the AWS MCP connector"* You will need AWS credentials configured on your machine. If you haven't already, set up your AWS credentials before adding this connector. ```bash theme={null} ultra add aws ``` The connector uses your local AWS credentials. Make sure `aws configure` is set up before adding. Connects your AI agent to Atlassian Jira for reading and managing issues, sprints, and projects. Ask your AI agent: *"Add the Jira MCP connector"* You will be prompted for your Jira instance URL and an **API token**. Create one at [id.atlassian.com/manage-profile/security/api-tokens](https://id.atlassian.com/manage-profile/security/api-tokens). ```bash theme={null} ultra add jira ``` When prompted, enter your Jira instance URL and API token. Connects your AI agent to your Mintlify documentation for searching and reading docs pages. Ask your AI agent: *"Add the Mintlify MCP connector"* You will need your Mintlify project subdomain or API key. ```bash theme={null} ultra add mintlify ``` When prompted, enter your Mintlify project details. Gives your AI agent access to Salesforce CRM data, objects, and reports. Ask your AI agent: *"Add the Salesforce MCP connector"* Salesforce uses OAuth. You will be redirected to Salesforce in your browser to authorize access to your org. ```bash theme={null} ultra add salesforce ``` Ultra handles the OAuth flow automatically. Authorize in your browser when prompted. Connects your AI agent to Datadog for querying metrics, logs, and monitors. Ask your AI agent: *"Add the Datadog MCP connector"* You will be prompted for your Datadog **API key** and **application key**. Find these in your Datadog org settings under API Keys. ```bash theme={null} ultra add datadog ``` When prompted, enter your Datadog API key and application key. Gives your AI agent read access to a PostgreSQL database for querying tables and schemas. Ask your AI agent: *"Add the PostgreSQL MCP connector"* You will be prompted for your database connection string. ```bash theme={null} ultra add postgres ``` When prompted, enter your PostgreSQL connection string (e.g., `postgresql://user:pass@host:5432/dbname`). Only connect to databases you are comfortable exposing to your AI agent. Use a read-only database user when possible. Lets your AI agent interact with your compliance data, controls, and vulnerabilities. Ask your AI agent: *"Add the Vanta MCP connector"* When prompted, authorize through your browser. Vanta uses OAuth, so you will be redirected to Vanta to grant access. ```bash theme={null} ultra add https://mcp.vanta.com/mcp --name vanta ``` Ultra detects Vanta's OAuth requirement and opens your browser to authorize. Once approved, the token is stored automatically. You need an active Vanta account with appropriate permissions. If you see an authentication error, check with your Vanta admin that your account has API access enabled. Gives your AI agent read/write access to specific directories on your machine. Useful for letting AI tools work with your project files. Ask your AI agent: *"Add the filesystem connector for my project files"* You will be asked which directories to allow access to. Only grant access to directories you want the AI to read/write. ```bash theme={null} ultra add filesystem ``` When prompted, specify the directory paths you want to allow. The connector runs locally and does not require authentication. The filesystem connector can read and write files. Only grant access to directories you are comfortable sharing with your AI agent. Never point it at your home directory or root. Don't see your connector listed? Check [Upstream Connectors](/configuration/upstream-connectors) for the full configuration guide. ## What's Next Full guide to connector configuration and management Learn about team management features Deep dive into traces and audit logs Understand the proxy architecture # MCP App Source: https://docs.ultra.security/getting-started/mcp-app Manage, discover, and reconnect your MCP connectors visually from inside your AI agent with the ultra_servers app Ultra ships an interactive **MCP App** — `ultra_servers` — that renders a connected-connector manager right inside AI agents that support MCP Apps. You can check connector health, discover and add new connectors, and reconnect connectors that need sign-in without leaving your conversation. The `ultra_servers` app renders in agents that support the open MCP Apps capability. In agents that don't, `ultra_servers` returns the same information as a plain text list, and every action is still available through the [`ultra_status`](/getting-started/ultra-mcp-tools#ultra_status), [`ultra_search`](/getting-started/ultra-mcp-tools#ultra_search), [`ultra_add`](/getting-started/ultra-mcp-tools#ultra_add), and [`ultra_reconnect`](/getting-started/ultra-mcp-tools#ultra_reconnect) tools. ## Opening the app Ask your AI agent to show your connectors, or call the `ultra_servers` tool directly: * *"Show my Ultra connectors"* * *"Open the Ultra connectors panel"* * *"Which of my MCP connectors need attention?"* In an MCP-Apps-capable agent, this opens the interactive panel. In other agents, you get a readable text list of the same connectors with their status and recovery hints. ## Connected tab The **Connected** tab lists every connector Ultra is proxying, with a header summarizing how many are healthy (for example, *"14 connectors · 12 healthy"*). Each connector row shows: | Field | Description | | - | - | | **Status** | `Healthy`, `Needs auth`, `Authorizing`, `Timed out`, or `Failed` | | **Transport** | How Ultra connects to the connector (`streamable-http`, `stdio`) | | **Auth method** | `OAuth`, `token`, `env credential`, or `no auth` | | **Tool count** | Number of tools the connector exposes through Ultra | A **Refresh** button re-fetches Ultra's current status snapshot so the list reflects the latest state — for example, after you reconnect a connector. It updates what's displayed; it does not re-probe or retry a failed or timed-out connector. Connector rows never expose secrets. The app renders from a sanitized snapshot that omits URLs, headers, environment variables, and credentials. ## Reconnecting a connector When a connector's authorization expires, its status changes to **Needs auth** and it stops exposing tools until you sign in again. How you recover depends on how the connector authenticates. ### OAuth connectors Connectors that authenticate with **OAuth** show a **Reconnect** button. Clicking it opens your browser to sign in again; once you finish, Ultra restores the connection with no restart of its own. Most agents then show the connector's tools again automatically; some only refresh their tool list on restart, so if the tools don't reappear, refresh or restart your agent. Behind the button, the app calls the `ultra_reconnect` tool. You can also trigger it directly in any agent — for example, *"Reconnect the Monaco connector"* — and Ultra returns a sign-in link to open in your browser. From a terminal, [`ultra reconnect `](/cli/reconnect) does the same re-auth for a single OAuth connector — useful when your agent can't surface the reconnect link. Unlike the in-agent reconnect, the CLI can't refresh a running agent, so **restart your MCP agent** afterward to load the new token. `ultra_reconnect` runs on **local devices** only; reconnecting a connector on a hosted device is not yet supported. ### Token and API-key connectors Connectors that authenticate with a **token**, **API key**, or **environment credential** can't be fixed by a browser sign-in. If one starts failing because its credential is invalid or expired, update the credential from the terminal instead: ```bash theme={null} # Replace the stored token ultra config set-token # Or update a custom auth header ultra config set-header ``` Unlike the OAuth reconnect above, updating a credential from the CLI doesn't refresh a running agent, so **restart your MCP agent** afterward for Ultra to reconnect the connector with the new credential. ## Discover tab The **Discover** tab searches the official MCP registry and npm, then lets you add a connector straight from the results. When a connector connects, it **hot-loads with no restart** — Ultra makes its tools available immediately. A connector that still needs OAuth sign-in or configuration, or that fails to hot-load, is saved and listed as non-healthy until you finish authenticating or fix its configuration. Discovery is backed by the [`ultra_search`](/getting-started/ultra-mcp-tools#ultra_search) and [`ultra_add`](/getting-started/ultra-mcp-tools#ultra_add) tools, so you can do the same thing conversationally (*"Search for a Postgres MCP connector and add it"*) in any agent. Only add connectors from official or trusted publishers. Untrusted connectors can run code on your machine (stdio) or receive data from your agents (HTTP). See [Upstream Connectors](/configuration/upstream-connectors) for guidance on vetting sources. Ultra hot-loads new tools with no restart of its own. Most agents pick up the new tools automatically; some agents (for example, Codex CLI) only refresh their tool list on reconnect, so if new tools don't appear, refresh or restart that agent or start a new conversation. ## Related The built-in tools the app is built on: status, search, add, migrate, and reconnect Add, configure, and manage MCP connectors from the agent or the terminal Connect Ultra to the AI agents that render the app Import MCP connectors you already have configured # Rollout Guide Source: https://docs.ultra.security/getting-started/rollout-guide How to implement full Ultra coverage across your organization Ultra runs next to your agent or harness. The tools your agents call route through it, so their activity is recorded and can be governed. Rolling Ultra out across an organization takes four steps: set up your organization, install onto devices, tailor your security controls, then monitor traffic. Each step has a page covering the mechanics. This guide is about the order and the decisions in between. ## 1. Set up your organization Create your organization, teams, and workspaces, then add members by invite, directory sync, or beacon. [Onboarding](/hub/onboarding) walks through all of it. [Workspaces](/hub/workspaces) group devices and their traces, and can receive targeted policies. Common workspaces include laptops, CI/CD, cloud agents, staging, and production, and they can just as easily follow teams, such as customer support and security. If organizing your devices that way would be useful, we suggest creating those workspaces up front, so guardrails and connectors can be scoped to a workspace when you reach step 3. If a single policy suits everything you run, one workspace is enough. Your organization starts with every built-in [guardrail](/hub/guardrails) in **Monitor** mode, [anomaly detection](/observability/anomaly-detection) on a 24-hour cadence, and governance posture at `default_allow`. Nothing is blocked or redacted until you change it. ## 2. Install onto devices Devices connect by browser sign-in, [deploy key](/hub/deploy-keys), or MDM. [Onboarding](/hub/onboarding) covers each, and [Install Ultra](/installation/install-ultra) covers the installer itself. Install on one device first and check it there before going wider. Ask your agent *"Call the Ultra Servers tool and show me my connected servers"*, have it use one of those connectors, then confirm the call appears in [Traces](/observability/traces). Then roll out in cohorts: your security team first, then one more team, then wider. Ultra picks up the connectors already configured in each agent on first start, so nobody reconfigures anything by hand. ## 3. Tailor your security controls Your organization is already running every built-in guardrail in Monitor. This step is where you turn that up and decide what agents can reach in the first place. Two controls, and most organizations want both. [Guardrails](/hub/guardrails) decide what an individual action is allowed to do. They inspect the action itself, so they work from the first call. Promote any guardrails as needed from **Monitor** to **Block** or **Redact**. [Governance](/hub/governance) decides which connectors and tools are reachable at all. It works from your inventory, so it covers what Ultra has already seen and widens as your catalog fills in. Posture sets the baseline for actions that no rule matches: `default_allow` permits everything except what you block, `default_deny` permits only what you approve. Set the organization to the most permissive posture you want, then tighten individual workspaces according to desired policy. Because governance depends on inventory, a connector nobody has used yet cannot be blocked by name, and `default_deny` usually lands later than your first guardrail promotions. ## 4. Monitor traffic Your [tool catalog](/hub/tools) fills in with the connectors and tools your agents actually reach for, and [Traces](/observability/traces) shows what they did with them. [Anomaly detection](/observability/anomaly-detection) scans that traffic on a schedule, looking for suspicious patterns and recording each finding with a risk score. It starts on a 24-hour cadence, adjustable down to 15 minutes if you want issues surfaced sooner. A new organization raises no alerts until you switch sources on, so open Configuration in [Alerts](/observability/alerts) and enable the guardrail, governance, and anomaly sources you want. As the catalog settles, come back to step 3: extend governance to cover what has appeared, and move a workspace to `default_deny` once its inventory looks stable. ## What's next Create your organization and bring people and devices onto it Enforcement modes, dry-run testing, and the built-in catalog Posture, allow and deny rules, and drift enforcement The guide to hand to everyone you are onboarding # Ultra MCP Tools Source: https://docs.ultra.security/getting-started/ultra-mcp-tools Manage MCP connectors directly from your AI agent using Ultra's built-in tools Because Ultra is itself an MCP server, it exposes built-in tools that your AI agent can call directly. This means you can search for, add, migrate, and monitor MCP connectors without leaving your conversation. These tools are available automatically when Ultra is connected to your AI agent. No extra configuration needed. These are Ultra's own built-in tools. For the inventory of tools your **upstream connectors** (GitHub, Linear, and so on) expose — with usage, enforcement, and blocking — see [Connector Tools](/hub/tools) in Ultra Hub. ## Available Tools Ultra provides these built-in tools: | Tool | What it does | | - | - | | `ultra_status` | Check Ultra's version, connected connectors, and total tool count | | `ultra_search` | Search the official MCP registry and npm for connectors | | `ultra_add` | Install a connector from a registry or URL, hot-loaded immediately | | `ultra_configure` | Set settings on a connector you already added, such as `client_id` or an access token | | `ultra_migrate` | Discover and import MCP connectors already configured in your agents | | `ultra_reconnect` | Re-authenticate a connector that needs sign-in, without restarting Ultra (some agents may need a refresh) | | `ultra_enable` | Turn a disabled connector back on and reconnect it | | `ultra_disable` | Stop using a connector for now, keeping its configuration and sign-in | | `ultra_remove` | Permanently delete a connector and its stored credentials | | `ultra_tools` | List the upstream tools available through Ultra right now | | `ultra_call` | Invoke one of those tools by name | | `ultra_servers` | **Ultra Connectors** — lists the connectors Ultra is connected to, and opens the interactive [MCP App](/getting-started/mcp-app) to manage them visually | `ultra_servers` renders an interactive panel to view, discover, add, and reconnect connectors visually. See the [MCP App](/getting-started/mcp-app) page. **Most of these need you to be signed in to Ultra Hub.** Only `ultra_status` and `ultra_migrate` work without it, matching the CLI commands they mirror. If a tool reports that it needs authentication, run [`ultra login`](/cli/login) and call it again — there is no need to restart your agent. In addition to these management tools, Ultra **aggregates all tools from your upstream connectors** and exposes them through a single connection. Each upstream tool is namespaced with its connector name, so two connectors offering a similarly named tool never collide. ## ultra\_status Check that Ultra is running and see what's connected. **Example prompts:** * *"Check my Ultra status"* * *"How many MCP tools do I have connected?"* **Returns:** Ultra version, number of upstream connectors, and total aggregated tool count. ## ultra\_search Search the official MCP registry and npm for connectors to add. **Example prompts:** * *"Search for a Notion MCP connector"* * *"Find MCP connectors for databases"* * *"Search npm for filesystem MCP connectors"* **Parameters:** | Parameter | Required | Description | | - | - | - | | `query` | Yes | Search term (e.g., "filesystem", "github", "database") | | `limit` | No | Max results to return (default: 10) | | `source` | No | Filter by `official` or `npm` | **Returns:** Connector names, descriptions, versions, authors, download counts, and required environment variables. ## ultra\_add Add an MCP connector and make its tools available immediately. No restart required. **Example prompts:** * *"Add the GitHub MCP connector"* * *"Install the filesystem connector so you can read my project files"* * *"Add the Slack MCP connector at [https://mcp.slack.com/mcp](https://mcp.slack.com/mcp)"* **Parameters:** | Parameter | Required | Description | | - | - | - | | `server` | Yes | Package name (e.g., `@modelcontextprotocol/server-filesystem`) or URL | | `name` | No | Custom name for the connector | | `env` | No | Environment variables (e.g., `{"GITHUB_TOKEN": "ghp_..."}`) | | `disabled` | No | Add in disabled state (default: false) | **What happens:** 1. Ultra resolves the connector from the registry or connects to the URL 2. Validates any required environment variables 3. Adds the connector to your Ultra config 4. Hot-loads the connector immediately so new tools are available without restarting Ultra hot-loads the connector with no restart of its own, and most agents pick up the new tools automatically. Some agents (for example, Codex CLI) only refresh their tool list on reconnect — if the new tools don't appear, use [`ultra_tools` and `ultra_call`](#ultra_tools-and-ultra_call) to reach them without restarting, or refresh that agent and start a new conversation. ## ultra\_migrate Discover MCP connectors already configured in your AI agents and import them into Ultra. **Example prompts:** * *"Migrate my existing MCP connectors to Ultra"* * *"What MCP connectors do I have that aren't going through Ultra yet?"* **Parameters:** | Parameter | Required | Description | | - | - | - | | `action` | Yes | `discover` to list available connectors, `execute` to migrate them | | `servers` | No | Specific connector names to migrate (execute only) | **What happens:** 1. Ultra scans your AI agents (Claude Desktop, Cursor, Codex, etc.) for configured MCP connectors 2. Shows you what it found, including command and connection details 3. On execute, imports selected connectors into Ultra's config and hot-reloads them 4. Duplicate connectors configured across multiple agents are merged automatically After migration, **restart your MCP agent(s)** so they route through Ultra instead of connecting directly. ## ultra\_configure Set settings on a connector you have already added. Use it when a connector needs credentials or configuration before it will work, for example a Slack `client_id` or a private app access token. Adding a connector is still [`ultra_add`](#ultra_add); this updates one that already exists. **Example prompts:** * *"Set the Slack client\_id and client\_secret on the slack connector"* * *"Put my HubSpot private app access token on the hubspot connector"* **Parameters:** | Parameter | Required | Description | | - | - | - | | `name` | Yes | The connector to update, as shown in `ultra_servers` | | `values` | Yes | One or more settings to apply. Each needs a `key`, a `value`, and a `type` | Each entry in `values` takes a `type`: | Type | Writes | Use for | | - | - | - | | `config` | A connector setting | Values the connector declares, such as `client_id` and `client_secret` | | `env` | A process environment variable | Command and stdio connectors that read credentials from the environment, such as `PRIVATE_APP_ACCESS_TOKEN` | An empty `value` clears that setting. **What happens:** Ultra reloads the connector's configuration from disk before writing, so edits you made by hand are not overwritten. Settings are saved, and an enabled connector hot-loads them without restarting Ultra. If the connector still needs browser sign-in afterwards, use [`ultra_reconnect`](#ultra_reconnect). If the hot reload cannot connect, Ultra says so and keeps the saved settings. Results name only the settings that were applied. Values are never echoed back, so secrets do not appear in your agent's transcript. Available on **local devices** only. Configuring connectors is not supported on a hosted device yet — see [Where each tool runs](#where-each-tool-runs). ## ultra\_reconnect Re-authenticate a connector whose sign-in has expired, in place. Used automatically by the **Reconnect** button in the [MCP App](/getting-started/mcp-app). **Example prompts:** * *"Reconnect the Notion connector"* * *"Which connectors need me to sign in again?"* **Parameters:** | Parameter | Required | Description | | - | - | - | | `name` | Yes | The connector to reconnect | **What happens:** For **OAuth** connectors, Ultra returns a sign-in link (and opens your browser when it can); once you finish, the connector's tools come back automatically. It applies to **local devices** only, and only to connectors that use browser sign-in — token or API-key connectors are recovered with `ultra config set-token ` instead. From a terminal, [`ultra reconnect `](/cli/reconnect) is the CLI equivalent for a single OAuth connector. It saves the new token but can't refresh a running agent, so restart your MCP agent afterward. ## ultra\_enable and ultra\_disable Turn a connector off without losing it, and turn it back on. `ultra_disable` disconnects a connector right away. Its tools stop working immediately, but its configuration and its stored sign-in are kept, so `ultra_enable` brings it back without re-adding it. If that sign-in expired or was revoked while the connector was off, `ultra_enable` returns a fresh sign-in link — so you may still be asked to sign in, but you never have to set the connector up again. **Example prompts:** * *"Disable the filesystem connector for now"* * *"Turn the Linear connector back on"* **Parameters:** | Parameter | Required | Description | | - | - | - | | `name` | Yes | The connector to enable or disable, as shown in `ultra_servers` | Use `ultra_disable` when you want a connector out of the way temporarily. To delete one for good, use [`ultra_remove`](#ultra_remove). ## ultra\_remove Permanently delete a connector: its configuration entry and any stored sign-in credentials are removed, and it disconnects right away. Re-adding it later means running [`ultra_add`](#ultra_add) and signing in again. **Example prompts:** * *"Remove the filesystem connector"* * *"Delete the old Jira connector and its credentials"* **Parameters:** | Parameter | Required | Description | | - | - | - | | `name` | Yes | The connector to remove, as shown in `ultra_servers` | | `confirm` | No | Must be `true` to actually remove. Omit it to preview instead (default: `false`) | **This is irreversible, so it takes two steps.** Called without `confirm`, `ultra_remove` only reports exactly what would be deleted. Call it again with `confirm: true` to apply. If you meant to keep the connector and just stop using it, use [`ultra_disable`](#ultra_enable-and-ultra_disable) instead. ## ultra\_tools and ultra\_call Some MCP agents do not refresh their tool list mid-conversation. When you add a connector or finish signing in to one, Ultra hot-loads it and announces the new tools, but an agent that ignores that announcement will not see them until it reconnects. `ultra_tools` and `ultra_call` are the way through. Because they are present from the start of the session, your agent can always call them, and they read Ultra's live catalog on every call. So you list what is actually available now, then invoke the one you want, without restarting anything. If you have just added a connector and your agent insists its tools don't exist, this is the pair to reach for. ### ultra\_tools Lists the upstream tools available through Ultra right now. **Example prompts:** * *"List the tools Ultra has available"* * *"What GitHub tools can you reach through Ultra?"* **Parameters:** | Parameter | Required | Description | | - | - | - | | `query` | No | Case-insensitive filter over tool name, title, and description | | `server` | No | Limit to one connector, for example `github` | | `limit` | No | Tools per call (default: 10, maximum: 25) | | `offset` | No | Where to start, for paging | **Returns:** each tool's namespaced name, title, description, and input schema, plus how many tools matched in total. When more remain, the response carries a `nextOffset` to pass back as `offset`. Narrowing with `query` or `server` is usually faster than paging. ### ultra\_call Invokes one tool returned by `ultra_tools`. **Example prompts:** * *"Use Ultra to search my GitHub repos for the deploy script"* * *"Call the Linear tool to list my open issues"* **Parameters:** | Parameter | Required | Description | | - | - | - | | `tool` | Yes | The tool's name, copied exactly as `ultra_tools` returned it | | `arguments` | No | An object matching that tool's input schema. Omit for a tool that takes none | The call runs through Ultra exactly as a direct call would, so governance, guardrails, and tracing all apply. The tool you select is a real upstream tool and may change external systems or data. ## Where each tool runs Ultra runs either on your own machine (a **local device**) or as enforcement Ultra operates for you (a **hosted device**). Most of these tools work in both places, but connector management that writes to local configuration does not yet. | Tool | Local device | Hosted device | | - | - | - | | `ultra_status`, `ultra_search` | ✅ | ✅ | | `ultra_tools`, `ultra_call`, `ultra_servers` | ✅ | ✅ | | `ultra_remove` | ✅ | ✅ | | `ultra_add` | ✅ | Remote connectors only — see below | | `ultra_migrate` | ✅ | ❌ | | `ultra_enable`, `ultra_disable` | ✅ | ❌ | | `ultra_configure` | ✅ | ❌ | | `ultra_reconnect` | ✅ | ❌ | `ultra_migrate` is local by nature: it reads the MCP configuration of agents installed on a machine and writes back to that machine's files. A hosted device has no view of your laptop, so there is nothing for it to discover. **`ultra_add` on a hosted device covers remote connectors only.** Adding one by URL works: Ultra discovers the connector's OAuth metadata, registers a client, and returns a sign-in link to finish in your browser. Adding by package name or registry identifier — the `@modelcontextprotocol/server-filesystem` form shown above — installs a connector that runs as a local process, so it applies to local devices only. Where a tool isn't available on a hosted device, it says so when you call it rather than failing quietly. ## Upstream Tool Aggregation Beyond its built-in tools, Ultra aggregates every tool, resource, and prompt from all your connected upstream connectors into a single MCP connection. Your AI agent sees one server (Ultra) with all tools available. Tools are automatically namespaced by connector name, so two connectors offering a similarly named tool never collide. Your agent sees the namespaced names in its own tool list, and [`ultra_tools`](#ultra_tools) reports the same names — use whichever your agent shows you rather than assembling one by hand. This means you can add a new upstream connector and its tools appear alongside everything else. No agent reconfiguration needed. Add and configure MCP connectors manually via CLI or config file Import existing connectors using the CLI wizard # How It Works Source: https://docs.ultra.security/how-it-works Understand Ultra's proxy architecture Ultra is a transparent proxy that sits between MCP agents and upstream MCP connectors. Every request and response passes through Ultra's pipeline, where it's traced, logged, audited, and measured. ## The Proxy Model ```mermaid theme={null} graph TD Agent["MCP Agent"] subgraph UltraBox[" "] direction TB UltraLabel["Ultra"] Transport["Transport"] Pipeline["Pipeline"] Aggregator["Aggregator"] UltraLabel ~~~ Transport Transport --> Pipeline Pipeline --> Aggregator end ConnectorA["Connector"] ConnectorB["Connector"] ConnectorC["Connector"] Agent -->|"MCP"| Transport Aggregator --> ConnectorA Aggregator --> ConnectorB Aggregator --> ConnectorC style UltraLabel fill:transparent,stroke:transparent ``` Your MCP agent sees Ultra as a single MCP server. Ultra connects to multiple upstream connectors and presents their combined tools, resources, and prompts as a unified interface. ## Key Components ### Transport Layer Ultra supports all MCP transport types: * **stdio** — Standard input/output (default for local agents like Claude Desktop and Codex) * **HTTP/SSE** — Server-sent events over HTTP * **Streamable HTTP** — Modern HTTP streaming transport The agent connects to Ultra over stdio, while Ultra connects to upstream connectors using whatever transport they require (stdio for local commands, HTTP for remote connectors). ### Pipeline Every MCP request passes through a chain of **interceptors** before reaching the upstream connector, and the response passes through the same chain in reverse order: | Interceptor | Purpose | | - | - | | **Trace** | Creates distributed traces for every request and response | | **Logging** | Structured request/response logging | | **Audit** | Records security audit events | | **Metrics** | Counters, histograms, latency tracking | | **Governance** | Determines which tool calls are allowed | | **Guardrails** | Evaluate each request and enforce policy | Policy enforcement runs alongside observability, before the request reaches the upstream connector. Enforcement is configured in [Ultra Hub](/hub/overview) and synced to every device. See [Guardrails](/hub/guardrails) and [Governance](/hub/governance). ### Aggregator The aggregator manages connections to multiple upstream MCP connectors and presents them as one: * **Tool namespacing** — Tools from different connectors are namespaced to avoid conflicts * **Routing** — Calls are routed to the correct upstream based on the tool/resource name * **Connection management** — Handles connection lifecycle across transports * **OAuth** — Supports OAuth2 authentication for upstream connectors that require it ### Storage Observability data is stored locally on your device by default, with optional sync to Ultra Hub for cloud deployments. ### Hub Sync When connected to Ultra Hub, your device periodically syncs traces and audit events to the cloud. The sync runs in the background with configurable intervals, and supports offline operation — data is stored locally and synced when connectivity is restored. ## What Gets Recorded For every MCP operation, Ultra records: * **Trace records** — Operation type, upstream connector, tool/resource name, request/response payloads, duration, status, and OpenTelemetry trace/span IDs * **Audit events** — Event type, severity, principal (agent identity), outcome (allow/deny/error), and detailed context * **Metrics** — Request counts, latency histograms, error rates, per-connector breakdowns ## Next Steps Learn about the interceptor chain Understand multi-connector routing # Admin Log Source: https://docs.ultra.security/hub/admin-log Security event log for administrative actions The Admin Log captures security-relevant events across your organization. It provides an audit trail for compliance and incident investigation. The Admin Log is restricted to users with **Admin** or **Owner** roles. See [Roles & Permissions](/hub/rbac) for details. ## Events Tracked The Admin Log captures the following events. Coverage is still expanding, and the gaps called out below are tracked for remediation; where an event is not yet recorded, treat your identity provider's logs as the authoritative record. ### Member Management * Member invited to organization * Member accepted invitation * Member invitation revoked * Member removed from organization * Member role changed (team-scoped only) Changing a member's **organization** role is **not** recorded today, including a promotion to Admin. Team-scoped role changes are. Use your identity provider's logs for organization-level role changes. ### Authentication * SSO login * SSO login failure * SSO configuration enabled or disabled Email-code (magic link) sign-in and account lockout are **not** recorded today. Use your identity provider's logs for those. ### Organization & Structure * Governance posture changed * Default provisioned role changed * Drift enforcement changed * Workspace updated Organization rename, slug and allowed-domain changes, organization create and delete, workspace create and delete, and all team create, update, and delete actions are **not** recorded today. ### Device Lifecycle * Device owner assigned * Device renamed * Diagnostics report uploaded (manual [`ultra doctor --report`](/cli/doctor) only) Device register, link, unlink, archive, and delete are **not** recorded today. ### Guardrail Configuration * Guardrail created (custom guardrails) * Guardrail updated (enforcement mode or configuration changed) * Guardrail deleted (custom guardrails) * Guardrail enabled * Guardrail disabled Creating, deleting, or disabling a security control is logged at **warning** severity to ensure visibility when protection is removed. Enabling or updating a guardrail is logged at **info** severity. Each guardrail event includes metadata describing what changed: | Field | Description | | - | - | | **Guardrail slug** | Human-readable identifier (e.g., `credential-protection`) | | **Category** | `builtin` or `custom` | | **Enforcement mode** | The post-change mode (`block`, `alert`, `monitor`, or `redact`) | | **Previous enforcement mode** | The pre-change mode, included only when the mode changed | | **Scope** | The level at which the guardrail is configured (`org`, `workspace`, or `gateway`) | ## Event Details Each event in the Admin Log records: | Field | Description | | - | - | | **Actor** | The user who performed the action (name and email) | | **Target** | The entity affected by the action (user, team, workspace, or device) | | **Action** | What was done (e.g., "member.invited", "role.changed") | | **Timestamp** | When the event occurred (UTC) | | **IP Address** | The IP address of the actor | ## Identity attribution Audit and trace entries in the Hub dashboard surface the user behind each event with three visual cues. The full attribution model is documented on the [Identities](/hub/identities) page. * **Assurance dot** — a small coloured dot next to the user's name indicates how the event was attributed. * **Green** (`authenticated`) — performed by a logged-in user with an active session. * **Amber** (`gateway`, shown as **Device-attributed**): attributed via the device's registered owner rather than a session. Typical for unauthenticated stdio traffic on a personal device. * **Grey** — no identity could be attributed. * **Role chip** — shows the user's organization role (`owner`, `admin`, `member`, or `viewer`) when known. * **Non-member badge** — appears when an event is attributed to a user who is not a current member of the organization. Use this to spot activity from offboarded employees or external identities that were never granted membership. Clicking the user's name in any drawer opens their identity detail page, which shows aggregate activity, peak hours, and any anomaly findings for that identity. ## Viewing the Admin Log The Admin Log is accessible from the Hub web interface: 1. Navigate to your organization in Hub 2. Select **Admin Log** from the sidebar 3. Browse events chronologically (newest first) Events can be filtered by action type, actor, target, and time range. # Agents Source: https://docs.ultra.security/hub/agents The AI agents your organization uses, like Claude, Cursor, and Codex, which can be monitored, observed, and governed via Ultra. The **Agents** page in Ultra Hub lists the AI agents your organization uses, such as Claude Desktop, Claude Code, Cursor, Codex, and VS Code. An agent appears once Ultra sees its traffic or finds it installed on a linked [device](/hub/devices). Open it from **Agents** under **Infrastructure** in the sidebar. ## Status **Status** shows whether Ultra manages the agent on every device it's installed on (**Managed**), on some of them (**Partial**), or on none (**Unmanaged**). Open the agent to see which devices it's on. To allow or block a specific agent, add a rule on the [Governance](/hub/governance) page. ## Agent detail Click an agent to open its page, with **Devices**, **Identities**, and **Versions** tabs. The **Versions** tab marks the newest version as **current**, and **Devices ↗** or **Identities ↗** on a version row filters those tabs to that version. # Authentication Source: https://docs.ultra.security/hub/authentication Authenticating with Ultra Hub Ultra Hub supports multiple authentication methods: web-based login, CLI authentication, and deploy keys for headless environments. For enterprise identity provider integration, see [Single Sign-On (SSO)](/hub/sso) and [SCIM Directory Sync](/hub/scim). ## Authentication Priority Ultra checks for authentication in this order: 1. **Web authentication** — Email + verification code (passwordless) 2. **CLI authentication** — `ultra login` (OAuth2 PKCE flow) 3. **Deploy keys** — `ULTRA_DEPLOY_KEY` environment variable or config file ## Web Authentication Ultra Hub uses passwordless authentication for the web interface. No passwords to remember or rotate. ### How It Works 1. Navigate to your Hub instance and click **Sign In** 2. Enter your email address 3. Check your inbox for a verification code 4. Enter the code to complete sign-in Sessions are maintained via secure cookies. You'll be prompted to re-authenticate when your session expires. ## CLI Authentication Authenticate from the command line using OAuth2: ```bash theme={null} ultra login ``` This opens your browser to complete authentication. After logging in, your credentials are stored locally in your Ultra config file and used for subsequent commands. ```bash theme={null} # Login to the default Hub ultra login # Login to a self-hosted Hub ultra login --hub https://hub.mycompany.com ``` ### How It Works 1. Ultra starts a local callback server on port 19284 2. Your browser opens to the Hub authentication page 3. You sign in (or create an account) 4. Hub redirects back to the local server with an auth token 5. The token is saved to your config file ### Session Management * Tokens are stored in `~/.config/ultra/config.yaml` * If your session expires, Ultra warns you and disables Hub sync * Re-run `ultra login` to refresh your session ### Check Login Status ```bash theme={null} # Shows your current auth state ultra link # Will tell you if you're not logged in ``` ## Deploy Keys For CI/CD pipelines, MDM fleet deployments, and other headless environments where browser-based login isn't possible, use [deploy keys](/hub/deploy-keys). Deploy keys are long-lived tokens scoped to a specific workspace that allow devices to authenticate without a browser login. See the [Deploy Keys](/hub/deploy-keys) page for setup and management. ## Account Lockout To protect against brute-force attacks, Ultra Hub enforces account lockout after repeated failed authentication attempts. If your account is locked, wait for the lockout period to expire before trying again, or contact your organization's admin. ## Logging Out ```bash theme={null} # Logout and clear credentials ultra logout # Logout without confirmation ultra logout --force ``` Logging out clears all Hub credentials and unlinks the device if it's currently linked. # Connectors Source: https://docs.ultra.security/hub/connectors Monitor MCP connectors across your organization The Connectors page in Ultra Hub provides visibility into all upstream MCP connectors used across your organization. See which connectors are active, how they're performing, and who's using them. ## Connector List The main view shows a card for each MCP connector with key metrics: | Metric | Description | | - | - | | **Connector Name** | The configured name of the upstream connector | | **Transport** | Badge showing the transport used to reach the connector (`stdio`, `http`, or `sse`) | | **Request Count** | Total number of MCP requests handled | | **Success Rate** | Percentage of requests that completed successfully | | **Unique Tools** | Number of distinct tools called on this connector | | **Avg Latency** | Average response time in milliseconds | | **Devices** | Number of devices routing traffic to this connector | | **Users** | Number of users interacting with this connector | Click any connector card to view detailed information. The transport badge is derived from the most recent trace for each connector, so it reflects how the connector is actually being reached. Older traces recorded before this field was captured appear without a badge. ### Blocking a connector Admins see a **Block** action on each row. Clicking it opens a confirmation dialog and creates an organization-wide rule that denies every tool call to that connector, for every user. Blocked connectors can be reviewed, edited, or unblocked from the [Governance page](/hub/governance). ## Connector Detail View The detail view provides in-depth metrics and breakdowns for a single connector. The connector's transport type (`stdio`, `http`, or `sse`) appears as a badge next to the connector name in the page header, making it easy to tell at a glance how Ultra is reaching the connector. ### Stats Overview At the top, summary cards show: * **Total Requests** — Lifetime request count * **Success Rate** — Overall success percentage * **Errors** — Total error count * **Tools** — Number of unique tools available * **Avg Latency** — Average response time ### Tools Tab A table of every tool called on this connector: | Column | Description | | - | - | | **Tool Name** | The MCP tool identifier | | **Calls** | Total number of times this tool was called | | **Success** | Number of successful calls | | **Errors** | Number of failed calls | | **Avg Latency** | Average response time for this tool | Use this to identify your most-used tools, spot error-prone tools, and find performance bottlenecks. ### Devices Tab Shows which devices are routing traffic to this connector: | Column | Description | | - | - | | **Device** | Device name (resolved from the device ID) | | **Requests** | Total requests from this device | | **Success** | Successful request count | | **Errors** | Error count | | **Avg Latency** | Average latency from this device | ### Users Tab Shows which users are interacting with this connector: | Column | Description | | - | - | | **User** | User name or email | | **Requests** | Total requests from this user | ### Requests Tab A full paginated trace history for this connector. Each entry links to the detailed trace view. Use **Load More** to page through older requests. In Hub mode, both this tab and the Overview preview show **who** made each request, linked to that user's identity. ## Use Cases * **Performance monitoring** — Identify slow connectors or tools that need optimization * **Usage analysis** — Understand which connectors and tools your team relies on most * **Troubleshooting** — Drill into error patterns by connector, tool, device, or user * **Capacity planning** — Track request volumes and device counts to plan infrastructure # Deploy Keys Source: https://docs.ultra.security/hub/deploy-keys Authenticate devices with Ultra Hub without browser-based login Deploy keys let an Ultra device authenticate with Ultra Hub without the person signing in through a browser. A key works the same way wherever the device is: a teammate's laptop, an [MDM fleet deployment](/installation/mdm-deployment), a CI/CD pipeline, or a server. They are the fastest way to bring a team onto Ultra. A deploy key removes the sign-in step for any teammate, whatever role they hold, so it pairs equally well with [beacons](/hub/rbac#beacon), invited members, and users provisioned from your directory. See [Rolling Out to Your Team](/hub/onboarding#rolling-out-to-your-team). ## How Deploy Keys Work A deploy key is a long-lived API token (prefixed with `dk_`) scoped to a specific workspace. When a device uses a deploy key, it: 1. Authenticates with Ultra Hub using the key instead of a browser login 2. Automatically links to the workspace the key is scoped to -- this happens during `ultra install --all` (for MDM deployments) or at `ultra start` time 3. Begins syncing policies, reporting telemetry, and enforcing guardrails Deploy keys are created by Owners or Admins in the Ultra Hub dashboard and can be distributed via MDM configuration profiles, environment variables, or other secure configuration management tools. ## Creating a Deploy Key Navigate to **Settings > Security** in the Ultra Hub dashboard. In the **Deploy Keys** section: 1. Enter a key name (e.g., "CI/CD pipeline", "Engineering fleet") 2. Select the **workspace** the key should be scoped to 3. Choose an **expiry** option (or select "No expiry" for permanent keys) 4. Click **Create Deploy Key** The key is displayed once after creation. **Copy it immediately** -- it cannot be retrieved later. The key starts with `dk_` and looks like: ``` dk_e679a4b22306f684da3bf0cfd8a0833f... ``` ## Using a Deploy Key ### Environment Variable Set the `ULTRA_DEPLOY_KEY` environment variable before starting Ultra: ```bash theme={null} ULTRA_DEPLOY_KEY=dk_your_key_here ultra start ``` ### Config File Add the key to your Ultra config file (`~/.config/ultra/config.yaml`): ```yaml theme={null} hub: deploy_key: "dk_your_key_here" ``` The environment variable takes priority over the config file value. ### Managed Preferences (MDM) For fleet deployments, set the `DeployKey` managed preference via your MDM solution: | Key | Value | Domain | | - | - | - | | `DeployKey` | `dk_...` | `com.ultra-security.ultra` | See the [MDM Deployment Guide](/installation/mdm-deployment) for the full setup. ## Pre-attaching connectors A deploy key can carry a set of connectors, so devices show up ready to work instead of each person adding connectors by hand. In the deploy keys table, the **Connectors** column shows **+ Attach Connectors**, or a count once some are attached. Either one opens the picker, where you choose the connectors the key should set up and fill in any values they need. Every device that enrolls with the key then arrives with those connectors already configured. ## Managing Deploy Keys The deploy keys table in **Settings > Security** shows all keys with their name, workspace, creation date, last used date, and expiry. Click **Revoke** to immediately invalidate a key. Revoking a deploy key disconnects all devices that rely on it for authentication. Those devices will need to re-authenticate using a new key or browser login. ## Identity Resolution A deploy key authenticates the device, not the person using it. Pair it with the device owner's work email and [SCIM directory sync](/hub/scim) to put a name on the activity: 1. Supply the device owner's email. The macOS and Windows installers ask for it as **Work email**, the terminal installer reads `ULTRA_DEVICE_EMAIL`, and MDM sets `UserEmail` in managed preferences 2. Ultra matches that email against the users provisioned from your directory 3. The device is attributed to that person for audit, policy, and RBAC See [Identity Resolution](/installation/mdm-deployment#identity-resolution) in the MDM Deployment Guide for details. ## Security Considerations * **Treat deploy keys like passwords** -- store them in secure configuration management, not in source code * **Scope keys narrowly** -- create separate keys per workspace or deployment group, rather than one key for everything. A single key is meant to be shared across the people reporting into that workspace, so you do not need one key per person * **Set expiry dates** -- for time-limited deployments, use expiring keys to limit exposure * **Revoke unused keys** -- regularly audit and revoke keys that are no longer needed * **Rotate keys** -- periodically create new keys and phase out old ones ## Permissions Deploy key management requires the `org:update` permission. Only **Owners** and **Admins** can create, view, or revoke deploy keys. See [RBAC](/hub/rbac) for the full permissions matrix. # Devices Source: https://docs.ultra.security/hub/devices Register and manage Ultra devices A **device** is an instance of Ultra running on a developer machine, CI server, or any other host. Devices link to workspaces in Ultra Hub and sync their telemetry data. ## Linking a Device After logging in, link your device to a workspace: ```bash theme={null} ultra link ``` The interactive flow: 1. Fetches your available workspaces from Hub 2. Lets you choose which workspace to link to 3. Prompts for a device name (defaults to your hostname) 4. Registers the device with Hub ### Non-Interactive ```bash theme={null} # Link to a specific workspace ultra link --workspace ws_xxxxxxxxxxxx # With a custom device name ultra link --name "My MacBook" ``` ## Flags | Flag | Short | Default | Description | | - | - | - | - | | `--workspace` | `-w` | | Workspace ID to link to | | `--name` | `-n` | hostname | Name for this device | ## Sync Lifecycle Once linked, the device syncs with Hub during `ultra start`: 1. **Startup** — Device connects to Hub and begins background sync 2. **Data sync** — Traces and audit events are synced at the configured interval (default: 60s) 3. **Heartbeats** — Sent at half the sync interval to report device health 4. **Token refresh** — OAuth tokens are refreshed automatically when needed ### Sync Configuration ```yaml theme={null} hub: sync_interval: "60s" # How often to sync offline_mode: false # Continue if Hub is unreachable ``` ### Offline Mode When `offline_mode: true`, Ultra continues operating normally even if Hub is unreachable. Data is stored locally and can be synced later. ## Unlinking a Device Remove the device's connection to Hub: ```bash theme={null} # Interactive (with confirmation) ultra unlink # Skip confirmation ultra unlink --force ``` Unlinking: * Notifies Hub that this device is disconnecting * Clears the device ID and workspace ID from local config * Preserves authentication tokens (for future re-linking) * Keeps local data intact ## Device Status Hub tracks each device's status: | Status | Description | | - | - | | **Online** | Device is syncing and sending heartbeats | | **Idle** | No heartbeat received recently — the device is between sync cycles or temporarily unreachable | | **Pending** | Device registered but hasn't synced yet | ## Auto-linking with deploy keys When a [deploy key](/hub/deploy-keys) is configured, the device links automatically without any manual `ultra link` step. This happens at one of two points: * **At install time** — `ultra install --all` (used by MDM deployments) and `ultra install --agent ona` link the device immediately, so it appears in Hub before any MCP agent opens * **At `ultra start`** — for all other paths (the interactive wizard, `ultra install --yes`, and per-agent installs other than Ona), the device links on first startup Both paths register the device to the workspace scoped by the deploy key. See the [MDM Deployment Guide](/installation/mdm-deployment) for the full setup. ## Linking via Hub Web UI You can also view and manage devices from the Hub web interface: 1. Navigate to your workspace in Hub 2. The **Devices** section shows all linked devices with their status (Online, Idle, Pending) 3. Device details include hostname, last sync time, and linked workspace Device registration currently requires the CLI (`ultra link`). The web UI provides monitoring and management of existing devices. ### Device detail panel Click any device row in the Devices list to open a slide-out detail panel with: * **Host** — Operating system, architecture (e.g., `ARM64`), hostname, device version, and device type (`Personal`, `Team`, etc.). * **Environment** — The environment the device runs in (`Local`, `Dev`, `Prod`, etc.). * **Activity** — Status (`Online`, `Idle`, `Offline`), last-seen timestamp, creation date, and linked workspace. * **Health**: **Enforcement** (the device's drift enforcement mode), **Last Scan** (when the device last scanned agent configs), **Fully Managed** (how many of the agents detected on the device are fully managed, e.g., `3 / 4`), and **Drift Detected** (the number of drift events detected). * **Agents** — A table of MCP agents connected to this device: | Column | Description | | - | - | | **Agent** | Agent name (for example, `Claude Code`, `claude-ai`) | | **Status** | Whether the agent is currently managed by Ultra | | **Version** | Reported agent version | | **Calls** | Number of tool calls routed through this device from the agent | | **Active** | Timestamp of the most recent activity | Use this view to confirm which host a device is running on, which workspace it belongs to, whether its configuration is in a healthy state, and which agents are talking to it. ## Re-Linking To move a device to a different workspace: ```bash theme={null} # Unlink from current workspace ultra unlink # Link to a new workspace ultra link --workspace ws_new_workspace_id ``` # Governance Source: https://docs.ultra.security/hub/governance Block specific MCP connectors, tools, and agents with organization-wide allow and deny rules The Governance page in Ultra Hub lets administrators block specific MCP connectors, tools, and agents across an organization, a workspace, or a single device. When a governance rule matches a tool call, the call is denied at the device before it ever reaches the upstream connector, and the denial is recorded in the [audit log](/observability/audit-log). Use governance when you need a fast, blunt control: removing access to a deprecated connector, cutting off a connector or agent during incident response, or sandboxing a newly registered integration while it is reviewed. ## How governance rules work A governance rule is an explicit allow or block decision keyed on two target types: **connectors** and **agents**. * **Connectors**: Tool calls to a specific upstream MCP connector. Add an optional tool filter to scope the decision to specific tools instead of every tool on that connector. * **Agents**: Tool calls originating from a named MCP agent (for example, `claude-desktop`, `cursor`, `claude-code`). You can also target an agent *type* (a whole category of agents) when you want the decision to apply to every instance of that agent. A rule can target connectors, agents, or both. Multiple values within the same target type are treated as "any of these." When both connector and agent targets are set, both must match for the rule to apply. When a governance rule matches, Ultra applies the rule's decision immediately, before the call reaches the upstream connector. Block rules deny the tool call and emit a deny event into the audit log. Allow rules let the call through governance and hand it to the rest of the pipeline (guardrails, anomaly detection, etc.). ### Connector identity Where Ultra can determine one, a connector's rule target is its durable identity. What serves as that identity depends on the connector: * A **remote** connector's identity is the canonical form of the URL Ultra dials. * A **stdio** connector's identity is the `source` coordinate declared in its config, so a rule follows the connector even when devices label it differently. * A stdio connector with **no `source`** has no identity a rule can match on, and is targeted by its local name. If the same connector is configured under different names on different devices, Hub treats it as one connector for governance and shows it as a single entry. A no-source stdio connector is the exception: the Governance page lists it once per configured name, each targeted by that name. ## Governance Posture Posture is the baseline admission decision for tool calls that **no rule matches**. It is set per scope and answers a single question: "If governance has no opinion, what should happen?" | Posture | Behavior for unmatched calls | When to use | | - | - | - | | **default\_allow** | Allow the call through. This is the historical behavior and the default for new organizations. | Permissive baseline. Rules act as a blocklist that carves exceptions out of "everything is allowed." | | **default\_deny** | Block the call. Only connectors, tools, and agents covered by an explicit allow rule can reach the upstream connector. | Zero-trust baseline. Rules act as an allowlist of pre-approved access. Use this when you want to ensure nothing reaches an upstream connector without being explicitly named. | Posture is set at the organization level as a baseline, and a workspace can tighten it. **A narrower scope can add restrictions but never remove them:** the effective posture is the stricter of the organization and workspace settings, so a workspace can move from `default_allow` to `default_deny`, but it cannot sit at `default_allow` beneath an organization on `default_deny`. Workspaces with no setting of their own inherit the org posture, and devices inherit from their workspace. A workspace value that is not currently the effective one is still stored. If the organization later relaxes its posture, that workspace value takes over. If a workspace genuinely needs an exception to an organization-wide `default_deny`, grant it as an allow rule created at that workspace's scope by an organization owner or admin, rather than by loosening the workspace's posture. Loosening the posture has no effect. The Posture section appears at the top of the Governance page with a status badge showing the effective posture for the current scope (and a note when the value is inherited from a higher scope). Owners and admins can flip the setting from the same panel, and the control is unavailable in both directions for a workspace whose organization mandates a stricter posture. That control is a dashboard affordance: what actually holds the mandate is posture resolution, not a rejection at the API. ## Drift enforcement Governance is also where you set **drift enforcement** for your organization: whether Ultra keeps itself installed in the MCP agents it finds on your members' devices, and how far it goes when it finds one it does not manage. A **Policy configuration** panel at the top of the Governance page shows the current posture and the current drift-enforcement mode. Everyone can see it; owners and admins get a **Manage** button. ### Setting it **Manage** opens the drift-enforcement dialog. Choose the scope first (**Organization** or **Workspace**), then the mode. The dialog offers the same five modes the CLI does (**Off**, **Detect only**, **Default**, **Auto install**, and **Enforce all**), each described under [drift enforcement](/cli/config#drift-enforcement) in the CLI reference. The dialog shows a one-line explainer as you select one. Two further options are not modes but ways of not setting one: * **Device setting** (organization scope) leaves drift enforcement to each device's own local configuration. * **Use organization setting** (workspace scope) clears a workspace's value so it follows the organization again. Drift enforcement is set at the organization or workspace level only; there is no per-device setting in Hub. A workspace value takes precedence over the organization's. **Enforce all** rewrites the configuration of MCP connectors your members did not add through Ultra. It suits a centrally managed fleet; set it deliberately. ### How it reaches devices A mode you set here overrides whatever a device has configured locally with [`ultra config set drift-enforcement`](/cli/config#drift-enforcement), including when the mode you set is **Default**. Devices pick it up on their next contact with Hub, but apply it **when Ultra next starts**. A device that is running keeps the mode it started with, so expect a lag between saving here and seeing the behavior change on a fleet. A device that is not linked to Hub follows its own local setting. Linking a device to a workspace clears any mode stored from a previous one. Changes are recorded in the [Admin Log](/hub/admin-log). ## Scoping Governance rules can be configured at three levels, exactly like guardrails: | Scope | Description | | - | - | | **Organization** | Applies to every workspace and device in your org. The default starting point for blanket policies. | | **Workspace** | Applies to every device in a workspace. Inherits all organization-scoped rules and adds to them. | | **Device** | Applies to a single device. Inherits all organization and workspace rules and adds to them. | Workspace and device rules **add to** the rules inherited from higher scopes, they do not replace them. Use the workspace and device filters at the top of the page to view rules in effect for a specific scope. Inherited rules appear faded in the table, with an indicator showing where they came from. ## Who can manage governance Only users with the **owner** or **admin** role can create, edit, move, or delete governance rules. Members and viewers can see the Governance page and the list of active rules, but the **Create rule**, **Edit**, and **Delete** controls are hidden. See [RBAC](/hub/rbac) for the full permission matrix. ## Creating a rule There are two ways to create a governance rule. ### Quick block from the connector controls The fastest way to block an entire MCP connector organization-wide. Navigate to **Governance** in the Ultra Hub sidebar and find the connector in the Browse list. From a connector's detail page under **Connectors**, the **Governance** button opens the same list focused on that connector. Use the connector's block control. The Connectors page itself is read-only in Hub and carries no block action. The dialog creates an organization-wide governance rule that denies every tool call to that connector, for every user in your organization. The rule appears immediately on the Governance page and is synced to all connected devices within seconds. ### Full rule editor from the Governance page Use the full editor when you need to target agents, filter to specific tools, or scope the rule to a workspace or device instead of the whole organization. Navigate to **Governance** in the Ultra Hub sidebar and click **+ Create rule**. Choose Organization, Workspace, or Device. The form previews exactly which agents the rule will apply to once active. Use a short, descriptive name (for example, `Block github-mcp` or `Sandbox new finance-mcp`). The name appears on the Governance page and in audit log events. In the **Connectors** section, pick one or more connectors from the dropdown. The list is populated from upstream connectors Ultra has seen across your organization. Click the tool summary on a connector row to optionally restrict the block to specific tools. Leave it as **all tools** to block the entire connector. In the **Agents** section, pick one or more agents to limit the rule to specific MCP agents. Leave both lists populated to require a match on both connector and agent. Leave the Connectors section empty to block every call from a listed agent regardless of which connector it targets. Click **Create rule**. The rule appears in the Governance table and syncs to every device in scope. ## Editing or moving a rule Click **Edit** on any rule you own at the current scope to change its name or its targets. Inherited rules are read-only at lower scopes; navigate to the scope they came from to edit them. To move a rule to a different scope, open it and click **Change scope...**. Picking a new scope creates the rule at the new scope and deletes the original. Both events are recorded in the audit log. ## Removing a rule Click **Delete** on the rule's row, then proceed through the confirmation dialog. Tool calls that the rule was blocking are allowed again after the change syncs to your devices, falling back to the active posture and any other explicit rules. Existing audit log entries for past denials are preserved. ## Transitioning to default\_deny Switching a scope from `default_allow` to `default_deny` is a meaningful change: every connector, tool, and agent without an explicit allow rule will immediately stop working in that scope. Ultra Hub helps you make the transition without breaking live traffic. Navigate to **Governance** in the Hub sidebar and select the scope (organization or workspace) you want to lock down. Click the posture control and choose **default\_deny**. A confirmation dialog opens listing every connector currently in use within the scope that does not yet have an allow rule. Confirm the dialog to auto-generate one allow rule per listed connector, scoped to the same place as the posture change. These rules are tagged as posture-generated so Ultra can clean them up later. The posture flips to `default_deny` once the rules are saved. Use the [Access view](#access-view) to see what is being allowed by the auto-generated rules versus by explicit policy. Replace the auto-generated rules with narrower allow rules (specific tools, specific agents) as you build out your allowlist, and delete the ones you no longer need. Flipping back to `default_allow` cleans up after itself: the dashboard removes the posture-generated allow rules it created at that exact scope, restoring the previous "rules as a blocklist" model. Rules you created by hand are left in place, and so is an inherited organization-scope posture-generated allow, which a workspace flip does not touch. If any of the cleanup does not go through, Hub tells you. A workspace cannot flip back to `default_allow` while its organization is on `default_deny`. The control is unavailable in that case, because the flip would delete the workspace's generated allow rules without changing what actually enforces. ## Audit log behavior Every governance decision generates a `governance` event in the [audit log](/observability/audit-log), shown under the **Governance Rule** filter in the dashboard. Each event records: * The decision (`outcome: deny` for blocks, `outcome: allow` for allow rules) * The reason (`explicit rule`, `default deny posture`, or the rule's name) * The MCP connector that was targeted * The tool the agent tried to call * The user and agent identity behind the call Governance decisions recorded before the dedicated event type shipped remain `guardrail` events and stay under the **Guardrail** filter; new decisions use `governance`. Use these events to confirm a decision is taking effect and to track who or what attempted to use a connector, tool, or agent after it was disabled. Posture changes themselves also emit a governance event so the audit trail captures every flip between `default_allow` and `default_deny`. ## Access view The **Access** panel on the Governance page is a per-connector view of how governance is treating live traffic in the selected scope. Each row carries: | Column | Description | | - | - | | **Connector** | The upstream MCP connector. | | **Status** | `Managed` when Ultra is the source of truth for the connector, `Unmanaged` when drift was detected on an agent, or `Partial` when some agents have it and others don't. | | **Access** | Allowed or Blocked, based on the effective rules and posture at the row's scope. | | **Reason** | One of: `Explicit allow rule`, `Explicit block rule`, `Default allow`, or `Default deny`. | | **Rule** | The matched governance rule, if the decision came from a rule rather than the posture. | | **Context** | Whether the decision is set directly at this scope or inherited from a parent. | Click a row to open the **Audit samples** drawer, which shows the most recent denied and allowed calls against that connector, with timestamps, the agent that initiated each call, and the IdentityLink for the user behind it. This is the fastest way to confirm a posture or rule change is having the effect you expected before you tighten further. ## Common scenarios | Scenario | Recommended action | | - | - | | Decommissioning an internal MCP connector | Quick-block the connector from the Connectors page, then remove it from your device configs once traffic has stopped. | | Vendor outage or compromise | Quick-block the affected connector, unblock once the issue is resolved. | | Blocking a connector only inside one workspace | Create a workspace-scoped block rule targeting that connector. The block applies inside the workspace and adds to any org-wide rules already in effect. | | Blocking a single dangerous tool but keeping the connector | Create a block rule with the connector selected and a tool filter listing only the tools to block. | | Sandboxing a new agent until reviewed | Create an agent-scoped block rule across the org while the integration is evaluated. | | Zero-trust for a sensitive workspace | Set the workspace posture to `default_deny`, accept the auto-generated allow rules for currently-used connectors, then narrow them down to specific tools and agents over time. | | Fine-grained matching on parameters or content | Use a [custom guardrail](/hub/guardrails) instead. Governance only matches on connector, tool, and agent identity. | ## Governance vs. guardrails Governance and guardrails answer different questions: | | Governance | Guardrails | | - | - | - | | **Question it answers** | Should this connector, tool, or agent be reachable at all? | Is this specific tool call safe? | | **Granularity** | Connector, tool, or agent identity | Tool name, parameters, content patterns, user, and more | | **Action** | Always block | Block, monitor, or redact | | **Scope** | Organization, workspace, or device | Organization, workspace, or device | If you need finer control, for example matching on parameter values, redacting matched content instead of blocking, or raising an alert without disrupting traffic (a guardrail in **Monitor** mode, switched on under **Alerts > Configuration**), use [custom guardrails](/hub/guardrails) instead. ## Related Enforce policies on tool names, parameters, and content Browse upstream connectors and quick-block from the table Review every governance and guardrail enforcement event Control who can manage governance rules # Guardrails Source: https://docs.ultra.security/hub/guardrails Enforce security policies on every MCP tool call with built-in and custom guardrails Ultra Guardrails let you define and enforce security policies across your MCP infrastructure. Every tool call passes through the guardrails engine before reaching upstream connectors, giving you real-time protection against credential leakage, destructive actions, input manipulation, and more. Guardrails are configured in **Ultra Hub** and automatically synced to all connected devices. Built-in guardrails are **recommended during configuration** and provide coverage for the most common MCP security threats once enabled at the appropriate scope. ## How Guardrails Work When an MCP tool call arrives at an Ultra device, the guardrails engine evaluates it against all active guardrails **in parallel**. Each guardrail inspects the request (tool name, parameters, connector context) and returns a decision. The strictest decision wins. ### Enforcement Modes Every guardrail runs in one of three enforcement modes: | Mode | Behavior | | - | - | | **Block** | Prevents the request from reaching the upstream connector. The agent receives a structured error with the guardrail name, the rule that fired, a redacted excerpt of the matched content, and a remediation hint so the agent can recover or surface a useful message to the user. | | **Monitor** | Allows the request and records the event. Use this for visibility without disrupting workflows. | | **Redact** | Allows the request after masking matched content in both the request and response. Matched values are replaced with `[REDACTED]`. | Ordered by how hard they enforce, the modes rank Monitor \< Redact \< Block. That ordering is what decides which rule wins when the same guardrail is configured at more than one scope. See [Scoping](#scoping). Guardrails fail closed for evaluation errors and unavailable evaluators during request-time enforcement. On the response side, an *evaluation error or missing evaluator* blocks only when the guardrail is configured in **Block** mode; in Monitor and Redact modes it allows the response and records an event instead. Two response-side paths block regardless of the configured enforcement mode, because Ultra cannot prove the content is safe: a response scan that exceeded its budget and was truncated, and a Redact-mode decision whose redaction fails. Separately, an enforcement mode Ultra does not recognize is itself treated as a block. A device that has no backend for an evaluator also skips it rather than failing every request closed, which is how Rate Limiting and Circuit Breaker behave on a hosted device. That follows from the deployment, not from a setting an operator turns on. See [Guardrail fail behavior](/concepts/secure-configuration-guide#guardrail-fail-behavior) for the full list. ### Scoping Guardrails can be configured at three levels: | Scope | Description | | - | - | | **Organization** | Applies to all workspaces and devices in your org. The place to set your baseline. | | **Workspace** | Applies to all devices in a workspace. | | **Device** | Applies to a single device. Most granular scope. | This lets you set an org-wide baseline that narrower scopes can tighten. **A narrower scope can add restrictions but never remove them:** a workspace or device rule is honored when it enforces harder than the organization's, and discarded when it would weaken it. Each guardrail has an **Enabled** toggle in its detail drawer, under **Status**. Disabling a guardrail stops enforcement but keeps its configuration, so you can turn it back on later without reconfiguring. #### How enabled and disabled rules resolve When the same guardrail is configured at multiple scopes, Ultra picks the rule that runs against live traffic as follows: * **An enabled rule always beats a disabled one.** A disabled rule at a broader scope is treated as unconfigured, so Ultra falls through to an enabled rule at a more specific scope. A disabled rule at a narrower scope cannot switch off a guardrail its organization has enabled. * **Among enabled rules, the strictest wins.** Monitor \< Redact \< Block. A workspace rule set to **Block** under an organization rule set to **Monitor** wins, because it tightens. A workspace rule set to **Monitor** under an organization **Block** is discarded, because it would relax the mandate. * **Scope only breaks a tie.** When two enabled rules enforce equally hard, the broadest scope owns the setting: organization, then workspace, then device. * **Disabled rules still apply when nothing is enabled.** If no enabled rule exists anywhere, the nearest (most specific) disabled rule becomes the effective configuration and the guardrail does not run. The winning rule brings its whole configuration with it. A narrower rule that tightens the mode also replaces the broader rule's other settings, so carry over anything you still want when you tighten at a workspace or device. **Rate Limiting is the exception.** Its rate limits accumulate instead of resolving to a single scope, so every limit set at every enabled scope applies and a workspace or device adds a tighter local cap rather than replacing the organization's. Circuit Breaker, despite also being stateful, follows the normal resolution rules. Rate Limiting and Circuit Breaker are enforced on **self-hosted devices only**. A hosted device has no shared counter store, so a guardrail of either type is skipped there rather than blocking. Every other guardrail behaves identically on both. Built-in guardrails are recommended in the catalog, but a built-in only runs once a rule enables it at the organization, workspace, or device scope. Catalog defaults describe the recommended starting configuration; they do not activate a guardrail on their own. A new organization is seeded with those rules automatically, so see [New Organization Defaults](#new-organization-defaults) for what it starts with. ### New Organization Defaults A newly created organization has every built-in guardrail enabled at the organization scope in **Monitor** mode automatically, alongside [anomaly detection](/observability/anomaly-detection). This gives you day-one visibility with no setup, but Monitor mode only records events: nothing is blocked or redacted until you promote a guardrail to a stricter mode. Organizations that existed before this change are not backfilled; enabling guardrails and anomaly detection there is still a manual step. Use [dry-run testing](#dry-run-testing) to see what each guardrail would catch, then follow [When to Change Enforcement Modes](#when-to-change-enforcement-modes) to move guardrails to Block or Redact. ## Built-in Guardrails Ultra ships with built-in guardrails that are **recommended during configuration**. These cover the most critical MCP security risks and become active only after you enable them at the organization, workspace, or device scope. You can view the full list, descriptions, and configuration options for each guardrail in the **Guardrails** page in Ultra Hub. | Guardrail | Category | Description | | - | - | - | | **Parameter Validation & Input Sanitization** | Input Protection | Validates tool call parameters against type constraints, range limits, and dangerous patterns like path traversal (`../`), dangerous CLI flags (`--exec`, `--privileged`), shell injection, and command chaining. Supports configurable allowed-directory restrictions and per-parameter blocklists. | | **Credential & Secret Protection** | Data Protection | Detects and blocks access to credential files (`.env`, `.aws/credentials`, `.ssh/id_*`, and more), secrets in tool parameters (API keys, tokens, private key headers), and cloud metadata endpoints. Supports automatic redaction of detected secrets: set the guardrail to **Redact** mode and matched secrets are masked in the outbound request, and in the response where those matches are echoed back. Redaction is not a separate per-guardrail setting, and this guardrail's default mode is **Block**. | | **In-Line Authorization & Destructive Action Blocking** | Access Control | Blocks destructive actions before they reach target systems. Covers destructive tool names (`delete_*`, `drop_*`, `rm_*`), dangerous SQL and shell operations in parameters, code/git write tools, and financial/payment tools. Each category can be independently enabled or disabled. | | **PII & Personal Data Protection** | Data Privacy | Detects personally identifiable information (PII) in tool call parameters and resource URIs, including email addresses, US Social Security numbers, phone numbers, credit cards (Luhn-validated), IBANs, cryptocurrency wallet addresses, BIC/SWIFT codes, and IPv4 addresses. Optional, default-off PHI detection covers labeled health identifiers and medical codes (medical record numbers, patient IDs, ICD-10, CPT, and HCPCS codes). Each category is independently toggleable, and a per-tool or per-resource bypass list lets you exempt internal tools that legitimately handle PII (such as HR or CRM lookups). | | **Tool & Server Isolation** | Isolation | Tracks the values an MCP session has read from one connector and blocks them from being written to a different connector in the same session. Stops cross-connector exfiltration patterns where an agent reads sensitive data from one tool and then sends it to a second one. Also supports a forbidden tool-sequence blocklist (for example, `github.read_repo → slack.post_message`). | | **Rate Limiting** | Infrastructure | Configurable request rate limits per MCP connector to prevent resource exhaustion and enforce usage quotas. Define rules with per-connector or wildcard limits using fixed time windows. | | **Circuit Breaker** | Infrastructure | Watches each upstream connector's error rate and latency and automatically blocks traffic to a connector that is failing or slow, then probes for recovery before restoring it. Protects against cascading failures and resource exhaustion during an upstream outage. | ### PII & Personal Data Protection The PII guardrail scans request parameters and resource URIs for personal data and emits a violation for each match. When run in **Redact** mode, matched values are replaced with `[REDACTED]` in the outbound request payload before it reaches the upstream connector, and in the response returned to the client. Redact mode protects the connector and the client. The raw matched values still sync to the Hub, so admin teams retain full visibility for incident response. **Default configuration.** The catalog recommends this guardrail in **Redact** mode, and it runs only once a rule enables it at the organization, workspace, or device scope. Once enabled, the high-precision categories are on by default (email, US SSN, phone numbers, credit cards with Luhn validation, IBAN, and crypto addresses). Lower-precision or specialized categories that frequently collide with non-PII identifiers are off by default and can be enabled per workspace: * **PHI / Health Information**: off by default. Detects labeled health identifiers and medical codes (MRN, patient IDs, ICD-10, CPT, and HCPCS codes). Label-anchored to avoid colliding with ordinary product, ticket, and billing identifiers. * **BIC / SWIFT codes**: off by default. Format collides with enum values, error codes, and UUID fragments. * **Public IPv4 addresses**: off by default. Many tool calls legitimately reference public IPs. * **Private IPv4 addresses**: off by default. Internal network references are rarely sensitive. **Response scanning.** Tool call responses are not scanned by default because most response payloads legitimately contain the PII the caller requested (HR or CRM lookups, customer records). Enable **Scan Responses for PII** when tools return untrusted content or when compliance requires bidirectional scanning. Response scanning is capped to the first 256 KB of body data with bounded JSON traversal. **Bypass list.** The `bypass_tools` list skips PII detection for matching tools or resources. Use exact names (e.g., `salesforce_search`) or prefixes ending in `*` (e.g., `workday_*`, `workday://*`). Matching is case-insensitive and applies to both request redaction and response scanning. Use this for internal tools that are expected to handle PII so the guardrail does not interfere with their normal operation. Some values (like long base58 hashes resembling Bitcoin addresses) can false-positive: start in **Monitor** mode if you are unsure how the guardrail will behave against your traffic. ### Tool & Server Isolation The Tool & Server Isolation guardrail stops cross-connector exfiltration patterns. It catches cases like an MCP agent that reads a secret from `aws-mcp.get_secret` and then posts it to `slack-mcp.post_message`. Detection is **session-scoped** and **stateful**: the guardrail watches the values flowing out of each tool response within a session and blocks subsequent calls on a different connector whose parameters contain those values. Two independent detectors run inside this guardrail: * **Cross-connector content detection** (`detect_cross_tool_content`, on by default). Every tool response is indexed against the session. If a later tool call on a *different* connector includes a value that matches indexed content from a previous connector, the call is blocked. Same-connector tool chains are exempt by default (`same_server_exempt`). * **Tool-sequence blocklist** (`tool_sequence_blocklist`). An explicit list of forbidden `(connector.tool → connector.tool)` patterns, each with a configurable `within_seconds` window. Use this to block known-bad chains regardless of payload contents (for example, "never let `github.list_secrets` precede `slack.post_message` within five minutes"). **Default configuration.** The catalog recommends this guardrail in **Block** mode, and it runs only once a rule enables it at the organization, workspace, or device scope. Once enabled, cross-connector content detection is on by default. The session content-flow tracker uses a default TTL of 300 seconds; supported range is 1 to 86,400 seconds. **Configurable knobs** (shown in the guardrail drawer with these labels): * **Detect Cross-Tool Content Flow** (`detect_cross_tool_content`): toggle the cross-connector content detector independently of the sequence blocklist. * **Content Flow Session TTL (seconds)** (`content_flow_session_ttl_seconds`): how long a session's indexed content is retained for matching. * **Blocked Tool-Call Sequences** (`tool_sequence_blocklist`): list of from/to entries describing forbidden tool transitions, each with an optional within-seconds window. * **Exempt Same-Server Flow** (`same_server_exempt`): when true (default), tool chains that stay on the same connector skip the cross-connector check. ### Circuit Breaker The Circuit Breaker guardrail protects availability. It watches each upstream connector's error rate and latency over a rolling window and trips when a connector looks unhealthy, blocking further calls to that connector until it recovers. This stops a degraded or down upstream from soaking up requests and triggering cascading failures. It runs as a three-state machine, per connector: * **Closed** (normal): calls flow through while Ultra records request and failure counts over the rolling window. * **Open** (tripped): once the failure rate or latency crosses the threshold, calls to that connector are blocked for the cooldown period, with a retry-after hint. The runtime error keeps the wire vocabulary: `circuit breaker open for server 'github-mcp': upstream is degraded, retry after 30s`. * **Half-open** (probing): after the cooldown the breaker admits a small number of probe requests. If they all succeed it closes and traffic resumes; if one fails it re-opens and the cooldown restarts. **Default configuration.** Off by default. Enable it where you want availability protection. Configure one or more rules, each targeting a connector (or `*` for a shared breaker across every connector), with these settings shown in the guardrail drawer: | Setting | Default | Description | | - | - | - | | **Connector** | | MCP connector to protect. Use `*` for a shared breaker across all connectors. | | **Window (seconds)** | `60` | Rolling window over which error rate and latency are measured. | | **Minimum Requests** | `20` | Minimum requests in the window before the breaker can trip, so small samples don't trip it. | | **Error Rate Threshold** | `0.5` | Fraction of failing requests (0 to 1) that trips the breaker once the minimum is met. | | **Latency Threshold (ms)** | `0` | Requests slower than this count as failures. `0` disables latency-based tripping. | | **Cooldown (seconds)** | `30` | How long the breaker stays open before probing for recovery. | | **Half-Open Probes** | `3` | Probe requests allowed while half-open. The breaker closes when all probes succeed and re-opens on the first failure. | Each rule can set its own enforcement mode; when omitted it inherits the guardrail's mode. If several rules trip at once, the strictest mode wins. ## Guardrail Categories Guardrails are organized into categories that map to different areas of MCP security: | Category | Description | Built-in Guardrails | | - | - | - | | **Input Protection** | Validates and sanitizes incoming tool call parameters | Parameter Validation & Input Sanitization | | **Data Protection** | Prevents credential leakage and secret exposure | Credential & Secret Protection | | **Data Privacy** | Detects and protects personally identifiable information, including optional health information (PHI) | PII & Personal Data Protection | | **Access Control** | Enforces authorization and blocks destructive actions | In-Line Authorization & Destructive Action Blocking | | **Isolation** | Tracks content flow across connectors within a session and blocks cross-connector data movement | Tool & Server Isolation | | **Output Safety** | Filters and validates tool call responses | None | | **Infrastructure** | Rate limiting, circuit breaking, and availability protection | Rate Limiting, Circuit Breaker | ## Choosing Your Guardrail Configuration ### Default Protection It is not what a new organization starts with. There, every built-in is enabled at organization scope in **Monitor** mode regardless of its catalog recommendation, so nothing blocks or redacts before anyone has reviewed it (see [New Organization Defaults](#new-organization-defaults)). In an organization created before that change, a built-in does not run at all until you enable it at a scope. Before adding custom guardrails or changing enforcement modes, consider your team's workflows. ### Scoping Flow When configuring guardrails, work from broad to specific: 1. **Start at the organization level**: Set your baseline security posture. The built-in guardrails belong here, and a new organization already has them all enabled at this scope in Monitor. Promote the ones you want enforcing to Block or Redact, and enable any that are missing if your organization predates that default. 2. **Tighten at the workspace level**: Use a workspace rule where a workspace has to enforce harder than the organization does, for example moving a guardrail from Monitor to Block. A workspace rule that would relax the organization's mode is discarded, so this is not the place to loosen an org-wide mandate. 3. **Tighten at the device level**: A device rule works the same way at the most granular scope. It is honored when it enforces harder than the workspace or organization rule above it, and discarded when it would weaken one. ### When to Change Enforcement Modes | Scenario | Recommended Mode | | - | - | | Production environments with sensitive data | **Block**: Prevent threats from reaching upstream connectors | | New deployment, or development and staging environments | **Monitor**: Record what would be caught, without disrupting workflows | | Handling sensitive data that must not appear in logs | **Redact**: Mask matched content in requests and responses | ### Adding Guardrails Beyond Defaults Consider enabling additional guardrails if your environment involves: * **Custom internal tools**: Create custom guardrails (see below) to enforce organization-specific policies on tool names, parameters, or connectors * **Multi-tenant environments**: Layer workspace-level and device-level guardrails to isolate tenants * **Compliance requirements**: Check the framework coverage tags on each guardrail to map your coverage against AARM, SAFE, and AUIC-1 frameworks * **Sensitive data workflows**: Switch credential protection to **Redact** mode if you need requests to proceed but want secrets masked. This guardrail defaults to **Block**, and Redact is looser than Block, so set it at the scope where the guardrail is currently configured. Setting it at a narrower scope has no effect, because a narrower scope can only tighten ## Custom Guardrails Beyond the built-in guardrails, you can create custom guardrails to enforce organization-specific security policies. Custom guardrails use the same evaluation engine and enforcement modes as built-in guardrails. ### Creating a Custom Guardrail 1. Navigate to **Guardrails** in the Ultra Hub sidebar 2. Click **+ Create Custom Guardrail** in the top-right corner 3. Fill in the guardrail details: * **Name**: A descriptive name (e.g., "Block /secrets directory access") * **Description**: What the guardrail does and why ### Trigger Conditions Trigger conditions define when the guardrail fires. Each condition has three parts: a **field**, an **operator**, and a **value**. You can add multiple conditions joined with AND logic using **+ Add condition (AND)**. **Available fields:** | Field | Description | | - | - | | **Tool Name** | The name of the MCP tool being called | | **Connector Name** | The upstream connector handling the request | | **Resource URI** | The resource being accessed | | **Any Parameter** | Matches against all parameter values | | **Parameter...** | Matches against a specific named parameter | | **User ID** | The authenticated user's identity ID. Empty string for unauthenticated requests. | | **User Email** | The authenticated user's email address. Empty string for unauthenticated requests. | | **User Name** | The authenticated user's display name. Empty string for unauthenticated requests. | | **Agent ID** | The MCP agent name from the connection handshake (e.g., `claude-desktop`, `cursor`). Empty string when unknown. | | **Agent Version** | The MCP agent version from the connection handshake (e.g., `1.2.3`). Empty string when unknown. | Trigger conditions match against values at any depth in the request parameters, not just top-level fields. If a tool call includes nested objects or arrays, the condition will evaluate against values found within them as well. User and agent identity fields are always present in the evaluation context. When the request is unauthenticated or the agent identity is unknown, these fields contain an empty string. Keep this in mind when writing regex conditions: patterns that match empty strings (like `.*`) will match unauthenticated requests. **Available operators:** | Operator | Description | | - | - | | equals | Exact match | | not equals | Does not match exactly | | contains | Value includes the specified string | | not contains | Value does not include the specified string | | starts with | Value begins with the specified string | | not starts with | Value does not begin with the specified string | | ends with | Value ends with the specified string | | not ends with | Value does not end with the specified string | | matches regex | Value matches a regular expression pattern | | in list | Value is one of a comma-separated list | | greater than | Numeric comparison | | less than | Numeric comparison | When matching against email domains, prefer **not ends with** over **not contains**. For example, `not ends with "@company.com"` correctly blocks `attacker@company.com.evil.com`, while `not contains "@company.com"` would let it through because the substring match succeeds. ### Enforcement Action Choose the enforcement action for your custom guardrail: * **Block**: Hard block with denial receipt. The request is stopped and the agent receives an error. * **Monitor**: Allow and record. The request proceeds and the event is recorded. * **Redact**: Mask sensitive data in transit. Matched content is replaced with `[REDACTED]`. ### Example: Block Access to Secrets Directory To create a guardrail that blocks any tool call attempting to access files in a `/secrets/` directory: 1. **Name:** Block /secrets directory access 2. **Description:** Prevents any tool from reading or writing files in the /secrets directory 3. **Trigger Conditions:** * Field: **Any Parameter** | Operator: **contains** | Value: `/secrets/` 4. **Enforcement Action:** **Block** ### Example: Flag Unrecognized Connectors To get notified when tool calls hit a connector outside your approved list: 1. **Name:** Flag unapproved connectors 2. **Description:** Flags tool calls routed to connectors not in the approved list 3. **Trigger Conditions:** * Field: **Connector Name** | Operator: **not equals** | Value: `approved-connector-1` * Field: **Connector Name** | Operator: **not equals** | Value: `approved-connector-2` 4. **Enforcement Action:** **Monitor** ### Example: Restrict command execution to internal users To block `execute_command` tool calls from users outside your organization: 1. **Name:** Block external users from execute\_command 2. **Description:** Only users with a @company.com email can run execute\_command 3. **Trigger Conditions:** * Field: **Tool Name** | Operator: **equals** | Value: `execute_command` * Field: **User Email** | Operator: **not ends with** | Value: `@company.com` 4. **Enforcement Action:** **Block** ### Deleting a Custom Guardrail To remove a custom guardrail you own, open its detail panel from the Guardrails page and click **Delete**, then proceed through the confirmation dialog. The action is restricted to owners and admins at the scope where the guardrail was created; inherited custom guardrails are read-only at lower scopes. Deleting a guardrail stops new evaluations immediately, but existing audit log entries for past evaluations are preserved. Built-in guardrails cannot be deleted: they can only be disabled or re-scoped. ## Dry-run testing Before enforcing a guardrail in production, you can dry-run it against historical traffic to preview what it would catch. Dry-run evaluates a guardrail against stored traces at the selected scope and returns which requests would have triggered, without affecting live traffic. Dry-run works for built-in and custom guardrails, whether they are currently enabled or saved as a disabled draft. Dry-run is not available for guardrails whose evaluator is stateful across requests (currently **Tool & Server Isolation** and any other content-flow guardrail). These guardrails record live session state during evaluation, so replaying them against historical traces would pollute the running session tracker and skew real-traffic decisions. The dry-run controls are hidden on the detail panel for these guardrails. ### Running a dry-run 1. Navigate to **Guardrails** in the Ultra Hub sidebar 2. Select a scope (organization, workspace, or device) and open a guardrail to reveal the **Test this guardrail** panel 3. Choose a range using the preset buttons or **Custom range** 4. Click the run button to evaluate the guardrail The panel header shows the **Target** scope and a status indicator (`Ready`, `Running`, `Draft ready`, `Save & test`, or `Unsaved`). #### Range selection Pick how much historical traffic to evaluate: | Range type | Presets | Custom range | | - | - | - | | **Last events** | 100, 250, 500 | 1 to 1000 events | | **Past time** | 1h, 1d, 3d | 1 to 168 hours | Open **Custom range** to enter an exact event count or hour value within the supported limits. #### Results Dry-run returns the following, which stay visible in the drawer until you change scope, switch guardrails, or run again: * **Traces evaluated**: Total number of historical traces tested * **Would trigger**: Number of traces that would have been caught * **Events**: Details of each trace that would have triggered. Each event shows: * The **action that would be taken** (block, monitor, or redact) * The **matched request context**: operation, upstream connector, tool name, and timestamp * A **human-readable explanation** of why the guardrail fired (for example, "path matched a path traversal rule") * **Trigger details** (expandable): each individual match that fired, showing the rule that triggered (for example, "Path traversal"), the field that was inspected (for example, "path"), and the matched value * **Evaluation errors**: Grouped error messages with sample trace IDs if any evaluations failed during the test ### Testing unsaved edits and disabled built-ins The dry-run panel can test guardrail configurations that are not currently live: * **Unsaved edits.** If you have pending changes, the button reads **Save & test**. Clicking it persists your edits and immediately runs a dry-run against the new configuration. * **Disabled built-in guardrails.** Built-in guardrails do not need to be enabled to be tested. When you adjust the configuration of a disabled built-in, the button reads **Save draft & test**: the panel saves the configuration as a disabled draft (no enforcement) and runs the dry-run against that draft. Use this to size the impact of a built-in before turning it on. A disabled draft has no effect on live traffic. It only stores the configuration so the dry-run engine has something to evaluate against. To start enforcing the draft, enable the guardrail from the detail panel. ### When to use dry-run | Scenario | Why dry-run helps | | - | - | | Enabling a new built-in guardrail | Save a disabled draft, dry-run it, and review the impact before flipping it on | | Tuning rate-limit thresholds | See how different `requests_per_window` and `window_seconds` values affect real traffic | | Changing enforcement modes | Understand what would change if you move from Monitor to Block | | Custom guardrail validation | Verify your trigger conditions match the intended requests | Start guardrails in **Monitor** mode, run a dry-run after a few hours of traffic, review the results, then promote to **Block** once you are confident in the coverage. ## Viewing Enforcement Events Every guardrail evaluation is recorded as a **GUARDRAIL** event in the audit log, regardless of outcome. You can view these in the **Audit Log** page alongside TOOL CALL events. ### Grouped evaluations When multiple guardrails evaluate a single request, their results share the same `trace_id` and are displayed as a single collapsible row in the audit log. The group row shows: * A **Guardrail \[N]** badge indicating how many evaluations occurred * The **strongest outcome** across all evaluations (deny > redact > monitor > allow) * The **highest severity** across all evaluations, which for guardrails means error > info. A block is recorded as ERROR and everything else as INFO, so the critical and warning levels the underlying comparator also ranks are not reachable from a guardrail evaluation Click the expand arrow to reveal every individual evaluation in the group. Each evaluation card shows the guardrail name, enforcement mode, severity, and match count. Click any card to open the full **Audit Detail** panel for that evaluation. This grouping keeps the audit log readable when many guardrails are active. A single tool call that triggers five guardrails appears as one row instead of five. ### Audit detail Clicking a guardrail event (or an individual evaluation inside a group) opens the **Audit Detail** panel, which shows: * **Event type and severity**: GUARDRAIL badge with INFO or ERROR severity * **Timestamp**: When the evaluation occurred * **Context**: The tool/action being evaluated, the MCP connector it targeted, the MCP agent that made the request, and the outcome (ALLOW, DENY, MONITOR, REDACT) * **User identity**: Name, email, and user ID of the person whose request triggered the evaluation * **Raw details**: Full JSON including the guardrail's enforcement mode, guardrail ID, which guardrail was evaluated (e.g., `parameter-validation`, `credential-protection`), type (builtin or custom), any matches, request context, and trigger details ### Severity mapping | Outcome | Severity | Description | | - | - | - | | **Allow** | INFO | Guardrail evaluated and found no issues | | **Monitor** | INFO | Guardrail matched but is in monitor mode | | **Redact** | INFO | Guardrail matched and masked sensitive content | | **Deny** | ERROR | Guardrail matched and prevented the request | Only a block raises the severity. Everything else a guardrail records, including a redaction, is INFO. A blocked call is recorded with the outcome **Deny**. Each tool call generates one guardrail event per active guardrail. These events share a `trace_id` and are grouped into a single expandable row in the audit log. ## Next Steps Scheduled security analysis of your MCP traffic View guardrail enforcement events alongside all MCP activity Control who can configure and view guardrails Monitor guardrail activity in the web dashboard # Identities Source: https://docs.ultra.security/hub/identities Every person, service account, and device making MCP tool calls through your fleet The Identities page in Ultra Hub lists every principal that has generated activity across your organization. An identity is the unit Ultra uses to attribute audit events, traces, and findings: a human user signed in through SSO, a service account, a device authenticated by a deploy key, or an unauthenticated MCP agent whose traffic is grouped by its handshake principal. The list shows who used which MCP connectors, which members are generating guardrail blocks, and what unattributed traffic is reaching your devices. Audit log, traces, traffic, and connector detail pages link to the identity detail page. ## Attribution model Ultra resolves the effective identity for every event using a three-tier fallback: | Tier | When it applies | Display | | - | - | - | | **Authenticated** | The event carries a Hub session user ID (SSO or SCIM-provisioned member). | Green dot, full name, role, member badge. | | **Device-attributed** | No session user, but the event references a device with a known owner. The device owner is credited. | Amber dot, owner's name, "Device-attributed" badge. | | **Unattributed** | Neither a session user nor a resolvable device owner is available. Grouped by MCP agent principal (e.g. `claude-desktop/1.2.3`). | Gray dot, agent principal as label, no link. | The same dot-color convention appears wherever the IdentityLink component renders (audit log rows, trace drawers, traffic tables, connector detail pages), so attribution is consistent across the entire dashboard. ## The Identities list Open **Identities** in the Hub sidebar to see every identity in your organization, sorted by most recently active by default. ### Columns | Column | Description | | - | - | | **Identity** | The name, email, or agent principal. Renders as a link to the Identity detail page. Non-org members are flagged with a "Non-member" badge. | | **Relationship** | "Member" with the assigned role (owner / admin / member / viewer), or "Non-member" for identities outside the org. | | **Role** | The organization role for members (Owner, Admin, Member, Viewer); empty for non-members. Also present in the export, where it is the column the [Secure Configuration Guide](/concepts/secure-configuration-guide) reads to count Owner accounts. | | **Assurance** | Authenticated, Device-attributed, Unattributed, or No activity. Reflects the highest assurance tier observed for the identity. | | **Devices used (30d)** | Number of distinct devices the identity routed traffic through in the last 30 days. | | **Tool calls (30d)** | Tool-call count over the last 30 days. | | **Blocks** | Guardrail deny outcomes attributed to the identity over the last 30 days. | | **Anomalies** | Approximate count of anomaly findings that named the identity. | | **Last active** | Most recent event timestamp, or "Never" for identities with no observed activity. | ### Filtering Click the filter icon to open the filter popover: * **Relationship:** Member or Non-member. * **Role:** Owner, Admin, Member, or Viewer. * **Assurance:** Authenticated, Device-attributed, Unattributed, or No activity. * **Has anomalies:** identities with at least one finding. * **Search:** substring match against name and email (case-insensitive). Filters compose with the column sort, so you can rank the riskiest unattributed traffic or the busiest members by tool-call volume. ### Unattributed activity The **Unattributed Activity** view (linked from the page header) lists traffic Ultra could not tie back to a Hub user. Entries are grouped by MCP agent principal so you can spot: * Shadow MCP agents running outside your managed device fleet. * Misconfigured agents whose device owner has been removed or transferred. * Service accounts that were never linked to a Hub identity. Each group shows the principal, the devices it reached, and the call volume. Use it as a punch list: each row should either be brought under management (assign a device owner, configure auth) or blocked via [Governance](/hub/governance) if it shouldn't be reaching Ultra at all. ## Identity detail Click any row to open the identity detail page. It pulls together the principal's profile, recent activity, and security signals in a single view. ### Header The page header carries the identity name with its assurance dot, the member badge and role, and the first-seen / last-seen window. ### Activity summary A grid of six cards summarizes the identity's last 30 days alongside a 90-day comparison: * **Tool calls:** total tool-call count. * **Connectors accessed:** distinct upstream MCP connectors reached. * **Unique tools:** distinct tool names invoked. * **Active days:** days with at least one event. * **Guardrail blocks:** deny outcomes, with the all-time total alongside the 30-day window. * **Attribution split:** how many events were authenticated vs. device-attributed. Hover the split for a tooltip explaining the layered model. ### Peak hours heatmap A 7×24 grid (day of week × hour, UTC) shading the times the identity is most active over the last 30 days. ### Guardrail blocks by type A breakdown of the identity's denies grouped by guardrail, using the same guardrail names shown on the [Guardrails](/hub/guardrails) catalog page (for example Parameter Validation & Input Sanitization, Credential & Secret Protection, or Tool & Server Isolation). Denies that cannot be mapped to a guardrail are bucketed under "unknown." ### Anomaly findings Recent [anomaly findings](/observability/anomaly-detection) that named the identity. Each finding shows the risk level, score, recommended action, and a relative timestamp. Anomaly attribution is approximate. Findings that named the identity by display name or email instead of its ID may not appear here. Treat this section as a strong hint rather than an exhaustive count. ## IdentityLink across the dashboard Wherever Ultra surfaces a principal (audit log rows, trace drawers, traffic tables, connector detail user lists), it renders the same IdentityLink component: * Colored dot for assurance tier (green / amber / gray) * Name (or email, or agent principal as fallback) * Role badge when the identity is a current member * "Non-member" badge when the identity is outside the org Clicking any IdentityLink opens the identity detail page in the same tab. Cmd/ctrl-click or middle-click opens the detail page in a new tab. This means every audit event or trace is one click away from the principal's full profile. ## Who can see Identities Every Hub member with read access to the organization (owner, admin, member, and viewer roles) can view the Identities list, the detail pages, and the unattributed view. There is no separate permission for Identities. If a user can see the audit log, they can see who generated each event. Membership changes (a member leaves the org, a new owner is invited) are reflected on the next page load. Past activity remains attributed to the historical identity even after the user has been removed, so audit history is preserved. ## Related Per-event activity with IdentityLink on every row Findings that surface identity-level risk patterns Policies that produce the block events attributed to each identity Roles and permissions that determine each identity's relationship to the org Recommended secure settings for administrative accounts, and a utility that compares this export against them # Onboarding Source: https://docs.ultra.security/hub/onboarding Setting up your organization in Ultra Hub When you first sign up for Ultra Hub, the onboarding wizard guides you through creating your organization structure. ## Onboarding Steps Choose a name and slug for your organization. The slug is used in URLs and API calls. * **Name**: Your company or team name (e.g., "Acme Corp") * **Slug**: URL-friendly identifier (e.g., "acme-corp") Creating the organization also turns on every built-in guardrail in Monitor mode and [anomaly detection](/observability/anomaly-detection) at a 24-hour cadence, both at the organization scope — see [New Organization Defaults](/hub/guardrails#new-organization-defaults). Nothing is blocked or redacted until you promote a guardrail to a stricter mode. Teams are groups within your organization. Start with one team and add more later. * **Name**: Team name (e.g., "Engineering") Workspaces contain devices and their telemetry data. Use workspaces to separate environments. * **Name**: Workspace name (e.g., "Development") * Typical setup: separate workspaces for Development, Staging, Production The wizard offers the Ultra download, along with the terminal install command, and points you at what to do next: * **Link a device**: install Ultra on your machine and connect it to your workspace * **Invite your team**: add team members to collaborate on policies and monitoring * **Read the docs**: policies, observability, and the rest of the platform ## Rolling Out to Your Team Bringing a teammate onto Ultra is two decisions, and they are independent: 1. **How they join your organization**, which sets their identity and role 2. **How their device connects**, which puts it in a workspace Mix and match freely. A beacon with no deploy key still signs in and links their own device. Someone invited as a Member can enroll with a deploy key and never sign in on the device at all. ### Adding people to your organization | Method | Role they get | Emails them | Best for | | - | - | - | - | | Directory sync | Your default role setting | No | Orgs already on a provider | | Invite | The role you pick | Yes | People who need the dashboard | | Add a beacon | Beacon, no dashboard | No | Covering a team quickly | #### Directory sync If you use Google Workspace, Okta, or another supported provider, [SCIM directory sync](/hub/scim) provisions members automatically instead of adding them one at a time. Set the role new users receive under **Settings > Security**. Ultra does not email provisioned users; they sign in through [SSO](/hub/sso). #### Invite Use an invite when the person needs to sign in to Ultra and see data. 1. Go to **Settings > Invites** 2. Click **Invite Member** 3. Enter the team member's email address 4. Select a role (Owner, Admin, Member, or Viewer). See [Roles & Permissions](/hub/rbac) for details 5. Click **Send Invite** The invited user receives an email with a link to join your organization. Accepting it settles their access. Connecting their device is the separate decision below. #### Add a beacon A [beacon](/hub/rbac#beacon) is a device-only membership. Their machine reports into Ultra; they get no dashboard access. 1. Go to **Settings > Members** 2. Click the **+** beside the Members heading and choose **Add beacon** 3. Enter the person's name and email Adding a beacon creates their membership without sending them anything. To give them dashboard access later, switch their role from Beacon to Viewer, Member, or Admin using the dropdown on their row in the **Members** tab. They keep the device they already linked. ### Connecting their devices | How the device connects | What the person does | | - | - | | Browser sign-in | Signs in once, when the browser opens on first start | | Deploy key | Enters the key once, during install or afterward | | MDM | Nothing. Enrollment is zero touch | #### Browser sign-in Every role can link its own device, Beacon included. They install Ultra, open their AI agent, and a browser sign-in appears on its own. Signing in is the whole job: the device links itself, and with one workspace there is nothing to choose. If you run several, the browser asks which one this device reports into. `ultra login` and `ultra link` do the same thing from the terminal, which is handy for re-linking a device or scripting a setup. Most people never need them. #### Deploy key A [deploy key](/hub/deploy-keys) does the connecting for them. There is no sign-in and no workspace to pick: the key already names the workspace, and the device links itself the next time Ultra starts. Your teammate can enter the key while installing Ultra or any time after. 1. Go to **Settings > Security**, find the **Deploy Keys** section, and click **Create Deploy Key** 2. Give it a name, pick the workspace your team should report into, and choose an expiry 3. Copy the key before closing the dialog 4. Optionally, use the **Connectors** column to attach connectors to the key, so devices arrive with them already set up. See [Pre-attaching connectors](/hub/deploy-keys#pre-attaching-connectors) 5. Share the key with your teammates through your usual secure channel, along with the [install steps](/installation/install-ultra) One key covers everyone in that workspace, so you do not need a key per person. See [Deploy Keys](/hub/deploy-keys) for every way a key can reach a machine. Ultra never emails a deploy key, and the key is shown only once when you create it. Copy it before closing the dialog, because it cannot be retrieved afterward. If you lose it, revoke the key and create a new one. #### MDM For managed fleets, your MDM hands each device its deploy key. Enrollment is the deploy key path from there, minus every manual step: nobody is given a key, and nobody has to enter one. See the [MDM Deployment Guide](/installation/mdm-deployment). ### Attributing devices to people The two decisions meet here. Linked devices match their owner's identity to the people within your Ultra organization. No match yet? The device still links and works normally, showing as **Unresolved Identity** until that person is in your organization. Add them later and the device attributes itself, along with the activity it already recorded. Order does not matter: enroll machines first or add people first, and you land in the same place. Each linked device automatically syncs traces and audit events to Hub, providing team-wide visibility into MCP tool usage. ## Organization Hierarchy ``` Organization (Acme Corp) ├── Team (Engineering) │ ├── Workspace (Development) │ │ ├── Device (Alice's MacBook) │ │ ├── Device (Bob's MacBook) │ │ └── Device (CI Runner) │ └── Workspace (Production) │ └── Device (prod-device-1) └── Team (Security) └── Workspace (Security Review) └── Device (security-scanner) ``` ```mermaid theme={null} graph TD Org["Organization
Acme Corp"] TE["Team
Engineering"] TS["Team
Security"] WD["Workspace
Development"] WP["Workspace
Production"] WSR["Workspace
Security Review"] G1["Device: Alice's MacBook"] G2["Device: Bob's MacBook"] G3["Device: CI Runner"] G4["Device: prod-device-1"] G5["Device: security-scanner"] Org --> TE Org --> TS TE --> WD TE --> WP TS --> WSR WD --> G1 WD --> G2 WD --> G3 WP --> G4 WSR --> G5 ``` # Ultra Hub Overview Source: https://docs.ultra.security/hub/overview Centralized management for agentic security Ultra Hub is the cloud control plane for managing agentic security across teams and organizations. It provides centralized visibility, device management, and team collaboration. ## What Hub Provides Register and monitor Ultra devices across your organization. Track which devices are online, their sync status, and health. Traces and audit events from all devices are synced to Hub for team-wide visibility. One dashboard for all MCP traffic. Manage organizations, teams, and workspaces. Invite members and control access. Group devices into workspaces for environment separation (e.g., Development, Staging, Production). ## Architecture ```mermaid theme={null} graph LR subgraph Devs["Developer Machines"] direction TB DEV1["Device (Alice)"] DEV2["Device (Bob)"] DEV3["Device (CI/CD)"] end subgraph Hub["Ultra Hub"] direction TB Org["Organization"] T1["Team"] T2["Team"] W1["Workspace"] W2["Workspace"] W3["Workspace"] HubD1["Device"] HubD2["Device"] HubD3["Device"] Dashboard["Dashboard
Traces · Audit"] Org --> T1 Org --> T2 T1 --> W1 T1 --> W2 T2 --> W3 W1 --> HubD1 W2 --> HubD2 W3 --> HubD3 HubD1 --> Dashboard HubD2 --> Dashboard HubD3 --> Dashboard end DEV1 -.->|sync| HubD1 DEV2 -.->|sync| HubD2 DEV3 -.->|sync| HubD3 ``` An organization can contain many teams, each with many workspaces, each with many registered devices. Each developer runs Ultra locally as a device. Devices link to a workspace and periodically sync traces and audit events to Hub. The Hub dashboard provides a unified view of all MCP activity. ## Getting Started Visit [hub.ultra.security](https://hub.ultra.security) and create an account. The onboarding wizard walks you through creating an organization, team, and workspace. ```bash theme={null} ultra login ``` ```bash theme={null} ultra link ``` ```bash theme={null} ultra start ``` Traces and audit events will now sync to Hub automatically. ## Hub API Hub exposes a REST API for device management: **Terminology:** the Hub API uses wire names that predate this naming and stay stable for backward compatibility. In paths and JSON fields, a **Device** is `gateway`, a **Connector** is `server`, and an **Agent** is `client`. Product docs and the UI use the new names; API paths and payloads keep the old ones. | Endpoint | Description | | - | - | | `GET /health` | Health check | | `GET /api/v1/organizations` | List organizations | | `POST /api/v1/organizations` | Create organization | | `GET /api/v1/organizations/:org/teams` | List teams | | `GET /api/v1/workspaces` | List workspaces | | `POST /api/v1/gateways/register` | Register device | | `POST /api/v1/gateways/:id/sync` | Sync device data | | `POST /api/v1/gateways/:id/heartbeat` | Device heartbeat | # RBAC Source: https://docs.ultra.security/hub/rbac Role-based access control (RBAC) in Ultra Hub Ultra Hub uses Role-Based Access Control (RBAC) to manage what users can do within your organization. Every user is assigned a role that determines their permissions across the Ultra Hub. Roles are ordered from broadest to narrowest access: **Owner > Admin > Member > Viewer > Beacon**. In practice, permissions are defined explicitly per role, so treat the matrix below as the source of truth for what each role can do. ## Roles ### Owner Full organization control including billing, settings, and member management. The user who creates an organization is automatically assigned the Owner role. Owner is the only role that can delete the organization. * Full access to all organization, team, workspace, and device operations * Can manage members (invite, remove, change roles) * Can delete the organization * Can manage all teams and workspaces * Only Owners can promote other users to Owner ### Admin Can manage teams, workspaces, and members. Admins are your organization's day-to-day managers who handle team structure and user onboarding. * Can view and update organization settings (cannot delete the org) * Can manage members: invite users, change roles (up to Admin), remove members * Can create, update, and delete teams and workspaces * Can manage devices (create, link, unlink, archive, delete) * Can view the [Admin Log](/hub/admin-log) * Can configure guardrails and [governance rules](/hub/governance) ### Member Can view and work within assigned teams and workspaces. Members are your standard users who interact with Ultra's security features daily. * Read access to organization, team, and workspace information * Can create, register, and link owned devices to workspaces * Can update workspaces and manage their devices, but cannot delete a workspace. Governance posture and drift enforcement are gated separately (`workspace:manage_guardrails` and `org:update`), so a Member cannot change those two settings from inside workspace update * An organization-level Member role resolves for every workspace in the organization, not only workspaces they were individually added to * Can sync device data and send heartbeats * Cannot manage other users or modify organization or team settings * Cannot view privileged materials (e.g., Admin Log) ### Viewer Read-only access to assigned teams and workspaces. Viewers can observe MCP traffic and security data but cannot make changes. * Read-only access to organization, team, and workspace information * Can link their own devices, and sync a device they own * Cannot manage devices they do not own * Cannot manage users or modify any settings * Cannot view privileged materials (e.g., Admin Log) ### Beacon Device-only role for machines that report into Ultra without their user needing dashboard access. Beacon users have zero dashboard access and exist for device linking and sync. * Exists to link a device and sync its data (traces, heartbeat) * No dashboard access * No direct access to organization, team, or workspace pages in the Hub An Owner or Admin adds a beacon from **Settings > Members** using the **+** menu, and Ultra sends the person nothing. The role limits dashboard access, not linking: a beacon signs in and links their own device like anyone else, or you pair it with a [deploy key](/hub/deploy-keys) so their device links itself on first start. Raise a beacon to a higher role any time from the role dropdown on their row. See [Rolling Out to Your Team](/hub/onboarding#rolling-out-to-your-team). ## Permissions Matrix | Capability | Owner | Admin | Member | Viewer | Beacon | | - | - | - | - | - | - | | View organization settings | ✅ | ✅ | ✅ | ✅ | ❌ | | Update organization settings | ✅ | ✅ | ❌ | ❌ | ❌ | | Delete organization | ✅ | ❌ | ❌ | ❌ | ❌ | | Create teams | ✅ | ✅ | ❌ | ❌ | ❌ | | Update/delete teams | ✅ | ✅ | ❌ | ❌ | ❌ | | Create workspaces | ✅ | ✅ | ❌ | ❌ | ❌ | | Update workspaces | ✅ | ✅ | ✅ | ❌ | ❌ | | Delete workspaces | ✅ | ✅ | ❌ | ❌ | ❌ | | Invite members | ✅ | ✅ | ❌ | ❌ | ❌ | | Manage member roles | ✅ | ✅ | ❌ | ❌ | ❌ | | Remove members | ✅ | ✅ | ❌ | ❌ | ❌ | | Create devices | ✅ | ✅ | ✅ | ✅ | ✅ | | Link own device | ✅ | ✅ | ✅ | ✅ | ✅ | | Unlink/archive devices | ✅ | ✅ | ❌ | ❌ | ❌ | | Delete devices | ✅ | ✅ | ❌ | ❌ | ❌ | | Sync device data | ✅ | ✅ | ✅ | ✅ | ✅ | | View dashboard & traffic | ✅ | ✅ | ✅ | ✅ | ❌ | | View Admin Log | ✅ | ✅ | ❌ | ❌ | ❌ | | Configure guardrails | ✅ | ✅ | ❌ | ❌ | ❌ | | View guardrails | ✅ | ✅ | ✅ | ✅ | ❌ | | Configure governance rules | ✅ | ✅ | ❌ | ❌ | ❌ | | View governance rules | ✅ | ✅ | ✅ | ✅ | ❌ | | View alerts | ✅ | ✅ | ✅ | ✅ | ❌ | | Configure alerts | ✅ | ✅ | ❌ | ❌ | ❌ | **Configure alerts** is about changing the configuration, not reaching it. Alerts and its Configuration view are both gated on the view permission, so a Member or Viewer can open Configuration and see the current switch states. Only an Owner or Admin can change them. **Create is ✅ for every role because a new device is always self-owned.** When a person registers or links a device interactively, the call requires only `workspace:link_gateway`, which every role holds, and the device's owner is set to the user who created it. Linking with a deploy key is a different path: the key carries its own workspace and authorizes the link itself, so no role check runs and the owner comes from the key rather than from a calling user. There is no pre-existing device for a role check to resolve against, so create does not go through the owner fast-path that the rows below do. **The link and sync rows are owner-scoped.** Ultra resolves device access by an owner check first and the workspace role second, so any role can act on a device it owns through the owner fast-path, even where the role itself does not carry the permission. This holds for unlink, archive, and delete too: a Member who owns a device can delete it, and so can an owner with no workspace membership at all. The **unlink/archive** and **delete** rows describe devices owned by *someone else*, which is where the role is what decides, and why those rows stop at Admin. ## Scope Hierarchy Permissions are enforced at three entity levels, and roles cascade downward: **Organization level**: Controls org-wide settings, member management, and billing. **Team level**: Controls team membership and workspace management within a team. **Workspace level**: Controls device registration, linking, and MCP traffic visibility. A user's role at the organization level automatically applies to all teams and workspaces within that organization. For example, an Org Admin has Admin permissions across all teams and workspaces without needing separate role assignments at each level. ## Managing Roles ### Via Hub Web UI Navigate to your organization's **Members** page in the Ultra Hub. Owners and Admins can change a member's role using the role dropdown next to each member. Role changes take effect immediately. **Important constraints:** * Only Owners can assign or remove the Owner role * Admins can assign roles up to Admin (not Owner) * You cannot change your own role * The last Owner of an organization cannot be demoted or removed ### Via Invitations When [inviting a new member](/hub/onboarding), select the role they should receive. The invited user will be assigned that role upon accepting the invitation. Roles available for invitation depend on the inviter's own role: * Owners can invite at any role level * Admins can invite at Admin level or below ### Default Role Assignment New users who join via invitation receive the role specified in the invitation. Users who create a new organization are automatically assigned the Owner role. For security, Ultra does not allow self-registration to an existing organization without an invitation. This prevents unauthorized users from gaining access. # SCIM Source: https://docs.ultra.security/hub/scim Automate user and group provisioning with SCIM 2.0 Ultra Hub supports SCIM 2.0 directory sync for automated user and group provisioning. When connected to your identity provider, SCIM keeps your Ultra Hub users and teams in sync with your corporate directory -- no manual user management required. ## How It Works Ultra receives webhook events from the SSO service whenever your identity provider makes SCIM changes (user created, group updated, etc.). This event-driven approach means changes propagate to Ultra Hub in near real-time. ``` Identity Provider → SCIM Protocol → SSO Service → Webhook → Ultra Hub (Polis) (sync to DB) ``` ### Setting Up Directory Sync Navigate to **Settings > Security** in the Ultra Hub dashboard. Under **Directory Sync**, you will see a setup link URL. Open this link in your browser to launch the guided configuration wizard. If someone else manages your identity provider, click **Copy** to copy the setup link and send it to them. They can complete the wizard without needing access to Ultra Hub. The wizard walks you through connecting your identity provider step by step. Select your directory provider from the list (Okta, Azure AD, JumpCloud, etc.) and follow the provider-specific instructions to configure SCIM provisioning. Once complete, the **Directory Sync** section in **Settings > Security** shows live connection status from your identity provider: * **Active** — The directory is connected and syncing with your identity provider * **Inactive** — The directory connection exists but has been deactivated * **Pending setup** — A setup link has been generated but the wizard has not been completed * **Not configured** — No directory sync has been set up When the directory first connects, you may see a message indicating it is waiting for the first sync from your identity provider. Once sync completes, provisioning stats appear: * **SCIM Users** -- Count of users provisioned via SCIM (out of total users) * **SCIM Teams** -- Count of teams provisioned via SCIM (out of total teams) View provisioned users in the **Members** tab and provisioned teams in the **Teams** tab. Setup links carry an expiration date, shown below the URL. Click **Revoke** to invalidate an active link and generate a new one at any time. **An expired link does not stop directory sync.** Once your directory is connected and active, sync keeps running whether or not the setup link is still valid — the link only exists to complete the wizard. Where a link has expired but the directory is active, Ultra replaces the expiry line with a warning marker on the URL rather than an error, because there is nothing wrong to fix. If you do need to run the wizard again, revoke the expired link and generate a new one. ## User Provisioning ### Creating Users When a user is created in your IdP's SCIM directory, Ultra Hub automatically: 1. Creates an identity in the authentication system 2. Creates a user account in Ultra Hub 3. Adds the user to your organization with the default role for provisioned users 4. Marks the user's onboarding as complete (SCIM users skip the onboarding wizard) Set **Default provisioned role** under **Settings > Security > Identity Provisioning**. It can be Beacon, Viewer, or Member, and new users receive Member if you have not set it. Directory events can never mint an Admin or Owner. The setting applies to memberships created by **SCIM and by enterprise SSO just-in-time provisioning**, and to new users only — existing members keep the role they already have, including after you change the setting. SCIM-provisioned users are tagged with a **SCIM** badge in the Members list and can immediately log in via [SSO](/hub/sso). ### Updating Users When a user's profile is updated in your IdP (name, email, active status), the changes sync to Ultra Hub automatically. If a user is deactivated in the IdP, Ultra deactivates their authentication identity first (blocking login immediately), then updates their Hub account. If you rename someone in your IdP and Ultra keeps showing the old name — or shows their email where a name should be — re-provisioning them updates it. Ultra matches them by email and refreshes the name rather than treating them as a new user. Where your IdP sends no name at all, Ultra keeps the name it already has instead of clearing it. ### Deprovisioning Users When a user is deleted from your IdP's directory, Ultra Hub **soft-deletes** the user: * The user's authentication identity is deactivated (they can no longer log in) * The user's account is marked as inactive * The user's data, audit trails, and team memberships are preserved Ultra never hard-deletes SCIM-deprovisioned users. This preserves audit trails and allows reactivation if the user is re-provisioned later. ### Reactivation If a previously deprovisioned user is re-provisioned in your IdP (e.g., an employee returns), Ultra Hub automatically reactivates their account **and their authentication identity**, so they can sign in again. The user regains access with their previous memberships intact. This applies whether your IdP sends the re-provisioning as an update or as a fresh create, which is what a directory does when someone is re-assigned the application. ## Group-to-Team Mapping SCIM groups from your identity provider map to **teams** in Ultra Hub. This lets you manage team structure from your IdP instead of manually creating teams in Ultra. ### Creating Groups When a group is created in your IdP, Ultra Hub creates a corresponding team in your organization. Initial group members are synced as team members. ### Updating Groups When a group is updated (renamed, members added or removed), Ultra Hub syncs the changes: * **Group rename** — The team name and slug are updated * **Full member sync** — On group update events, the team membership is reconciled against the IdP's member list. Members not in the IdP list are removed; new members are added. * **Incremental member changes** — Individual member add/remove events are processed immediately without a full reconciliation If a team was created by a group push and its roster came through empty, re-push the group from your identity provider. A group update event is authoritative and will populate it. Rosters are not backfilled retroactively, so a team that came across empty stays empty until your IdP sends it again. ### Deleting Groups When a group is deleted in your IdP, Ultra Hub **does not auto-delete** the corresponding team. This prevents accidental data loss from IdP misconfigurations. A warning is logged, and the team can be manually removed by an admin if needed. SCIM-provisioned teams are tagged with a **SCIM** badge in the Teams list. ## Idempotency and Ordering Ultra Hub handles duplicate and out-of-order webhook events gracefully: * **Deduplication** — Each webhook payload is assigned a deterministic delivery ID. If the same event is received twice, the duplicate is silently ignored. * **Stale event detection** — Events include a signature timestamp. If an event's timestamp is older than the last processed event for that user, it is skipped. This prevents out-of-order events from reverting newer changes. * **Upsert semantics** — User and group creation events are idempotent. If the user or team already exists (matched by external ID or email), the event is treated as a no-op or update. ## Webhook Security SCIM webhooks are secured with HMAC-SHA256 signature verification: * Every webhook request includes an `Ory-Polis-Signature` header with a timestamp and HMAC signature * The signature is computed over the timestamp and request body using a shared secret * Ultra Hub verifies the signature and rejects requests that fail verification * **Replay protection** — Webhook timestamps older than 5 minutes are rejected, preventing captured payloads from being replayed If no webhook secret is configured, Ultra Hub rejects all SCIM webhooks. The secret is configured automatically during SSO setup. ## Supported Events | Event | Action | | - | - | | `user.created` | Creates authentication identity + Hub user + org membership | | `user.updated` | Updates user profile; handles activate/deactivate transitions | | `user.deleted` | Soft-deletes: deactivates identity, marks user inactive | | `group.created` | Creates team with initial member sync | | `group.updated` | Updates team name + full member reconciliation | | `group.deleted` | Logs warning (no auto-delete to prevent data loss) | | `group.user_added` | Adds user to team (incremental) | | `group.user_removed` | Removes user from team (incremental) | ## Viewing SCIM status Navigate to **Settings > Security** in the Ultra Hub dashboard. The **Directory Sync** section displays live connection status pulled from your identity provider: | Status | Meaning | | - | - | | **Active** | Directory is connected and syncing | | **Inactive** | Directory connection exists but is deactivated | | **Pending setup** | Setup link generated, wizard not yet completed | | **Not configured** | No directory sync configured | When the directory is active and users have been provisioned, the section also shows provisioning stats: * **SCIM Users** -- Count of users provisioned via SCIM (out of total users) * **SCIM Teams** -- Count of teams provisioned via SCIM (out of total teams) If the directory shows as **Active** but no users or teams have synced yet, a message indicates that the connection is established and waiting for the first sync from your identity provider. View individual SCIM-provisioned users in the **Members** tab and SCIM-provisioned teams in the **Teams** tab. Both display a **SCIM** badge next to provisioned entities. ## Audit Trail All SCIM provisioning events are recorded in the [Admin Log](/hub/admin-log) as security events: * `scim.user_provisioned` — New user created via SCIM * `scim.user_updated` — User profile updated via SCIM * `scim.user_deactivated` — User deprovisioned via SCIM * `scim.group_created` — Team created from IdP group * `scim.group_updated` — Team updated from IdP group * `scim.group_deleted` — IdP group deletion received * `scim.group_member_added` — User added to team via SCIM * `scim.group_member_removed` — User removed from team via SCIM * `scim.webhook_failed` — Webhook processing error (includes error details) ## Supported Directory Providers | Provider | Protocol | | - | - | | Entra ID (Azure AD) | SCIM v2.0 | | Google Workspace | Native integration | | JumpCloud | SCIM v2.0 | | Okta | SCIM v2.0 | | OneLogin | SCIM v2.0 | | Generic SCIM 2.0 | SCIM v2.0 | Any identity provider that supports the SCIM 2.0 protocol can be connected using the Generic SCIM 2.0 option. Google Workspace uses a native integration rather than the SCIM protocol. # Single Sign-On (SSO) Source: https://docs.ultra.security/hub/sso Configure enterprise SSO for your organization Ultra Hub supports enterprise Single Sign-On (SSO) via SAML and OIDC identity providers, as well as Google social sign-in. SSO allows your team to authenticate using your organization's existing identity provider instead of email verification codes. ## SSO Options Ultra supports two types of SSO: * **Enterprise SSO**: SAML or OIDC connections with identity providers like Okta, Azure AD, Google Workspace, and others. Included on all plans. * **Google Sign-In** — "Continue with Google" for organizations using Google Workspace. Available on all plans. ## Enterprise SSO Enterprise SSO integrates with your organization's identity provider via SAML 2.0 or OpenID Connect (OIDC). Ultra delegates IdP protocol handling to its SSO service, so your users authenticate at their familiar corporate login page. ### Supported Providers | Provider | Protocol | | - | - | | Auth0 | SAML | | Azure AD (Entra ID) | SAML | | Google Workspace | SAML | | JumpCloud | SAML | | Microsoft AD FS | SAML | | Okta | SAML | | OneLogin | SAML | | PingOne | SAML | | Rippling | SAML | | Generic SAML 2.0 | SAML | | OIDC Provider | OpenID Connect | Any SAML 2.0 or OIDC-compliant identity provider can be connected using the generic options. ### Setting Up Enterprise SSO Navigate to **Settings > Security** in the Ultra Hub dashboard. Under **Enterprise SSO**, click **Generate Setup Link** to create a self-service configuration URL. The setup link URL appears with a **Copy** button and a **Revoke** button. Copy this link and share it with your IT team if they manage your IdP. The link opens a guided wizard where they can enter your IdP details (metadata URL for SAML, or discovery URL and client credentials for OIDC). Setup links have an expiration date displayed below the URL. You can revoke an active link and generate a new one at any time. Open the setup link and follow the guided wizard. The wizard provides provider-specific instructions for each supported identity provider, including the exact values to enter in your IdP's admin console. Select your provider from the list and follow the step-by-step instructions within the wizard to complete the connection. After completing the wizard, the connection details appear in the **Enterprise SSO** section of the Security tab showing: * **Connection** -- The connection name (e.g., "Ultra SSO Setup") * **Type** -- The protocol badge (SAML or OIDC) * **Status** -- The connection status (ACTIVE when working) Click **Test Connection** to verify the SSO flow works before rolling it out to users. If something is misconfigured, you can click **Disable SSO** and re-do the setup. ### SSO Login Flow Once SSO is enabled, the login experience changes based on your policy settings: 1. A user enters their email address on the Ultra Hub login page 2. Ultra detects the user's email domain and looks up the matching SSO connection 3. The user is redirected to their identity provider to authenticate 4. After successful authentication, the user is redirected back to Ultra Hub with an active session If the user doesn't exist in Ultra Hub yet and [JIT provisioning](#just-in-time-provisioning) is enabled, their account is created automatically on first login. **Existing users**: If a user previously signed in with a magic link (email code) and then SSO is enabled, their account is automatically linked on first SSO login. No manual action is required -- the user signs in through the IdP and their existing Ultra Hub account is preserved with all data intact. ### Policy Settings Configure SSO behavior in **Settings > Security > SSO Policy**. **Enforce SSO** — When enabled, users with matching email domains must authenticate via SSO. Magic link (email code) login is blocked for those users. This ensures all authentication goes through your IdP. **Just-in-Time (JIT) Provisioning** — When enabled, new users who authenticate via SSO are automatically provisioned in Ultra Hub with the Member role and added to your organization. When disabled, users must be pre-provisioned (via [SCIM](/hub/scim) or manual invitation) before they can log in. **Required Domains** — Email domains that must use this SSO connection. Users with matching domains are routed to SSO. If left empty, domain routing falls back to the organization's allowed domains setting. Before enabling **Enforce SSO**, verify that your SSO connection is working correctly using the **Test Connection** button. If the connection is misconfigured, users with matching domains will be unable to log in. ### CLI Authentication with SSO The Ultra CLI supports SSO authentication: ```bash theme={null} # Standard login (auto-detects SSO if available for your email domain) ultra login # Explicit SSO login ultra login --sso ``` When SSO is detected for your email domain, `ultra login` redirects through your identity provider in the browser. The `--sso` flag skips email entry and goes directly to SSO authentication. ### Managing SSO Connections Once SSO is enabled, the **Enterprise SSO** section in **Settings > Security** shows the active connection with its type and status. From here you can: * **Test Connection** -- Verify the SSO flow is working by triggering a test authentication * **Disable SSO** -- Remove the SSO connection and revert users to magic link authentication To update the SSO configuration (e.g., change SAML metadata or IdP settings), generate a new setup link and re-run the wizard. The new connection replaces the existing one. ## Google Sign-In Google sign-in provides a lightweight SSO option for organizations using Google Workspace. Users see a **Continue with Google** button on the login page and can authenticate with their Google account. ### How It Works 1. User clicks **Continue with Google** on the Ultra Hub login page (or enters an email with a matching domain) 2. User authenticates with their Google account 3. Ultra Hub creates or links their account and starts a session ### Configuring Google Sign-In Navigate to **Settings > Security > Google** to configure: * **Enforce for domains** — Require users with matching email domains to sign in with Google instead of magic links * **JIT provisioning** — Automatically create accounts for new users who sign in with Google * **Domains** — Email domains for Google Sign-In enforcement and JIT provisioning (e.g., `yourcompany.com`) Public email domains (gmail.com, outlook.com, yahoo.com, etc.) cannot be used for SSO domain enforcement. Only corporate/organizational domains are accepted. ### Domain Validation When configuring SSO domains (for both enterprise SSO and Google sign-in), Ultra enforces two levels of validation: 1. **Public email blocklist** — Common free email providers (Gmail, Outlook, Yahoo, etc.) are rejected to prevent accidental enforcement on personal accounts 2. **Allowed domains subset** — If your organization has an allowed domains list configured, SSO domains must be a subset of that list ## Security Ultra SSO includes several security measures: * **CSRF protection** — Login flows use cryptographic state parameters to prevent cross-site request forgery * **Flow expiration** — SSO login flows expire after 10 minutes. Expired flows are automatically cleaned up * **Webhook signature verification** — SCIM webhooks are verified using HMAC-SHA256 signatures with timestamp validation to prevent replay attacks * **Deactivated user blocking** — Users deactivated via SCIM or by an admin are blocked from SSO login even if their IdP session is still valid ## Permissions SSO configuration requires the `org:update` permission. Only **Owners** and **Admins** can manage SSO settings. See [RBAC](/hub/rbac) for the full permissions matrix. # Teams Source: https://docs.ultra.security/hub/teams Group people and workspaces in Ultra Hub Teams group people and workspaces around how your organization operates. Each team has its own members and its own workspaces. Manage teams from **Settings > Teams**. Owners and Admins can create teams and add members. ## Create a Team Click **Create Team** and enter a **Name**. The **Slug** is generated from the name if you leave it empty. To add a workspace to a team, open the team and click **Create Workspace**. ## Add a Member 1. Open the team from **Settings > Teams**. 2. Under **Members**, click **Add Member**. 3. Choose a person and a **Role**, then click **Add member**. You can add eligible existing organization members. To add someone new, invite them from **Settings > Invites** first. You can assign your own role or a lower one. ## SCIM-Managed Teams Teams synced from your identity provider show a **SCIM** badge. Their membership is managed by your identity provider, so **Add Member** isn't available. To add someone to a SCIM team, add them to the matching group in your identity provider. Ultra Hub picks up the change on the next sync. Name and Slug edits made in Ultra Hub are also overwritten on the next sync. See [SCIM](/hub/scim#group-to-team-mapping) for how groups map to teams. # Connector Tools Source: https://docs.ultra.security/hub/tools A cross-connector inventory of every MCP tool your upstream connectors expose, with usage, enforcement, and blocking The Connector Tools page in Ultra Hub is a cross-connector inventory of every MCP tool your organization has used. Where the [Connectors](/hub/connectors) page is organized by upstream connector, the Connector Tools page flattens that into one row per tool, so you can see every tool in one place: how heavily each is used, how often its calls are blocked, and which tools are tied to anomalies. It is the fastest way to find a specific tool across all your connectors and to shut down a risky one organization-wide. This page is the inventory of tools exposed by your **upstream connectors** (GitHub, Linear, and so on). For Ultra's own built-in tools — `ultra_status`, `ultra_add`, `ultra_reconnect`, and more — see [Ultra MCP Tools](/getting-started/ultra-mcp-tools). ## Tool inventory Each row is a unique connector and tool pair. The table merges live usage stats with enforcement data: | Column | Description | | - | - | | **Tool** | The MCP tool name. A **Blocked** badge marks a tool that an organization governance rule is currently blocking; a **Connector blocked** badge marks a tool whose entire connector is blocked. | | **Connector** | The upstream MCP connector that exposes the tool. | | **Calls** | Total number of times the tool has been called. | | **Success Rate** | Percentage of calls that succeeded, color-coded (green at 95% and above, amber at 80% and above, red below). | | **Blocked** | How many calls a guardrail or governance rule has blocked. | | **Anomalies** | How many anomaly findings reference this tool. | | **Agents** | Number of distinct MCP agents that have called the tool. | | **Avg Latency** | Average response time. | | **Last Active** | When the tool was last called. | Tools that have only ever been blocked (every call denied, with no successful traffic) still appear, with zeroed stats, so a tool you blocked before it was ever used does not vanish from the inventory. Sort by any column (the default is **Calls**, highest first), filter by **Tool** name or **Connector**, and group by tool or connector. Use **Export** to download the inventory as CSV or JSON, and the **Copy link** button in a tool's detail panel to share a deep link to it. See the [dashboard guide](/observability/dashboard) for how export and shareable links work. ## Tool detail Click any row to open the tool's detail panel. Its header carries a copy-link button and, for Hub admins, **Govern** and **Block tool** actions. Below that: * **Overview**: calls, success rate, average latency, distinct agents, and the tool's blocked and anomaly counts. * **Security**: recent blocked calls (each with the policy that blocked it and the agent behind the call, plus a link to view them in the audit log) and recent anomaly findings. * **By agent**: how many calls each MCP agent has made to the tool. * **Recent calls**: the latest traces for the tool, with a link to view them in Traffic. The panel is also where you act on a single tool: use **Govern** to jump to its governance rules, or **Block tool** / **Unblock tool** to act on it directly. ## Blocking a tool Blocking is available to **owners and admins in Ultra Hub**. On a standalone device, the Connector Tools page is read-only. The Connector Tools page is wired into [Governance](/hub/governance), so you can stop a tool without leaving the inventory. * **Block a single tool** from its detail panel. This creates an organization-wide governance rule that denies that tool on that connector, and the row picks up a **Blocked** badge. * **Block several tools at once** by selecting their rows and choosing **Block** in the selection toolbar. A confirmation dialog summarizes how many tools will be blocked before the rules are created. * **Unblock** works the same way, individually or in bulk, for tools that a tool-level rule is blocking. Tools carrying a **Connector blocked** badge are blocked by a connector-wide rule instead; unblock those from the [Connectors](/hub/connectors) or [Governance](/hub/governance) page. * **Govern** opens the Governance page focused on the tool's connector when you need a more specific rule (particular agents, or a workspace or device scope) than a blanket block. Blocks created here are ordinary governance rules: they sync to every device in seconds, and every decision is recorded in the [audit log](/observability/audit-log). ## Use cases * **Find a tool fast.** Search the whole fleet for a tool by name instead of opening connectors one at a time. * **Spot risky tools.** Sort by Blocked or Anomalies to surface the tools driving enforcement and anomaly activity. * **Shut down a tool organization-wide.** Block a dangerous or deprecated tool in one click and confirm the denials in the audit log. * **Right-size governance.** Start from a blanket block here, then narrow it to specific agents or scopes from the Governance page. ## Related Browse upstream connectors and their per-tool breakdowns Allow and deny rules for connectors, tools, and agents Enforce policies on tool names, parameters, and content Review every block and enforcement decision # Workspaces Source: https://docs.ultra.security/hub/workspaces Organize devices into workspaces Workspaces are containers for devices and their telemetry data within Ultra Hub. Use workspaces to organize your MCP infrastructure by environment, team, or purpose. ## What's in a Workspace A workspace contains: * **Devices** — Ultra instances linked to this workspace * **Traces** — MCP operation traces synced from linked devices * **Audit events** — Security audit events from linked devices * **Connector statistics** — Aggregate data about upstream MCP connectors * **[Admin Log](/hub/admin-log)** — Security events for administrative actions within the workspace ## Typical Organization ``` Organization └── Team (Engineering) ├── Workspace: Development │ ├── alice-macbook (device) │ ├── bob-macbook (device) │ └── charlie-macbook (device) ├── Workspace: Staging │ └── staging-ci (device) └── Workspace: Production ├── prod-gw-1 (device) └── prod-gw-2 (device) ``` ### Common Patterns * **By environment** — Development, Staging, Production * **By team** — Frontend, Backend, Data Science * **By project** — Project Alpha, Project Beta ## Selecting a Workspace When you run `ultra link`, you select a workspace for your device: ```bash theme={null} # Interactive — shows a list of your workspaces ultra link # Direct — link to a specific workspace ultra link --workspace ws_xxxxxxxxxxxx ``` ## Workspace Data Isolation Each workspace's data is isolated: * Traces from Workspace A are not visible in Workspace B * Audit events are scoped to the workspace * Device health is tracked per workspace * Statistics and aggregations are workspace-scoped This makes workspaces suitable for environment separation where development data shouldn't mix with production data. ## Managing Workspaces Workspaces are currently managed through the Hub web interface. During onboarding, you create your first workspace. Additional workspaces can be created from the Hub dashboard. # Agent Environments Source: https://docs.ultra.security/installation/agent-environments Run Ultra inside cloud agent dev environments so MCP traffic flows through Ultra automatically Ultra integrates with [Ona](https://ona.com) cloud dev environments so that every MCP call made by the Ona Agent flows through Ultra. Configure a deploy key first; then add the devcontainer so install and migration run automatically with no manual steps inside the container. ## Linking to your Ultra Hub tenant The devcontainer hooks (below) get Ultra proxying MCP traffic locally. To send traces and audit events to your Ultra Hub tenant, the environment needs to authenticate. ### Deploy key Use a [deploy key](/hub/deploy-keys) in Ona’s environment variables, because interactive `ultra login` is not available in Ona cloud dev environments. This path is zero-touch for end users and works well for team and fleet deployments. In Ultra Hub, go to **Settings → Security → Deploy Keys** and create a workspace-scoped deploy key. See [Deploy keys](/hub/deploy-keys) for details. In your Ona organization's environment variables or secrets, add: | Variable | Description | | - | - | | `ULTRA_DEPLOY_KEY` | The deploy key you created | Rebuild any active Ona environments. On first boot, Ultra auto-links a device for each environment, attributed to the correct user. Traces appear in the Hub without any per-user login step. ## Quick start Add a `.devcontainer/devcontainer.json` to your repo (or merge these hooks into an existing one): ```jsonc theme={null} { "postCreateCommand": [ "bash", "-lc", "curl -sSL https://get.ultra.security/install.sh | bash && ultra install --agent ona --yes && { ultra migrate --from ona --all --yes 2>/dev/null || true; }" ], "postStartCommand": [ "bash", "-lc", "ultra migrate --from ona --all --yes 2>/dev/null || true" ] } ``` For environments that must authenticate `install.sh` before execution, use the [verified install flow](/installation/install-ultra#verify-the-install-script-signature) in the devcontainer hook. Rebuild your Ona environment. Ultra is now proxying all MCP traffic. ## What happens on first boot The `postCreateCommand` runs once when the environment is built: 1. **Installs the Ultra binary** into `/usr/local/bin/ultra` via the install script. 2. **`ultra install --agent ona --yes`** writes the repository-local `.ona/mcp-config.json` (typically `/workspaces//.ona/mcp-config.json`), creating the `.ona/` directory if missing, and adds `ultra` to `mcpServers`. Existing entries are preserved. This holds even when Ona's devcontainer lifecycle runs before `ONA_WORKSPACE_ID` is set, because an explicit install falls back to the enclosing Git repository. 3. **`ultra migrate --from ona --all --yes`** moves any pre-existing MCP connector entries from `.ona/mcp-config.json` into Ultra's upstream config (`~/.config/ultra/config.yaml`), so the Ona Agent only sees `ultra` and every tool call routes through it. The `postStartCommand` runs on every subsequent environment start. It re-runs migrate to catch any MCP connectors you added to `.ona/mcp-config.json` between sessions. Both commands are idempotent. After the hooks finish, `.ona/mcp-config.json` looks like: ```json theme={null} { "mcpServers": { "ultra": { "command": "/usr/local/bin/ultra", "args": ["start"] } } } ``` ## Verification After rebuilding, confirm Ultra is wired correctly: ```bash theme={null} # Ultra binary is installed ultra version # From anywhere inside the repository, Ona's MCP config points at Ultra repo_root="$(git rev-parse --show-toplevel)" && cat "$repo_root/.ona/mcp-config.json" # Run diagnostics ultra doctor -v ``` If you configured a deploy key, traces should appear in your Ultra Hub dashboard within about 60 seconds of the Ona Agent making an MCP tool call. ## Merging with an existing devcontainer If your repo already has a `.devcontainer/devcontainer.json` with its own lifecycle hooks, chain the Ultra commands onto the end: ```jsonc theme={null} { "postCreateCommand": [ "bash", "-lc", " && curl -sSL https://get.ultra.security/install.sh | bash && ultra install --agent ona --yes && { ultra migrate --from ona --all --yes 2>/dev/null || true; }" ] } ``` The Ultra binary is self-contained, so order only matters if your setup installs tools Ultra depends on (it doesn't). ## Troubleshooting The Ona detector reports `Installed=true` only when `ONA_WORKSPACE_ID` is set or the `ona` CLI is on `PATH`. Ona's devcontainer lifecycle can run before that signal is set, so this warning can appear inside an Ona environment as well as outside one. It is a warning, not a failure. An explicit `ultra install --agent ona` treats the agent you named as authoritative: it prints the path it is about to configure and continues. The only thing that stops an explicit Ona install is failing to resolve that config path, covered in the next entry. Ultra writes the repository-root file Ona reads (`/workspaces//.ona/mcp-config.json` on a default Ona VM), never a file under the process working directory or at the filesystem root. An explicit `ultra install --agent ona` can create the file even when Ona's lifecycle does not set `ONA_WORKSPACE_ID`, provided it runs inside the repository you want configured. It writes to the enclosing Git root, so any subdirectory of that repository works. Other operations stay reuse-only outside an Ona environment. Passive detection, drift checks, `ultra migrate`, and cleanup reuse an existing `.ona/mcp-config.json` but never create one, so create the file in the repository first if you are relying on those. Ultra tries several roots in order and only reports this when every one fails. It first reuses an existing `.ona/mcp-config.json` found by walking up from the working directory, then takes the enclosing Git root, and only then looks under `/workspaces`. Run the command from inside the repository you want configured, with a `.git` directory at its root. Because the enclosing Git root is chosen before the `/workspaces` scan, several repositories under `/workspaces` are harmless as long as you run the command inside the intended one. The `/workspaces` scan decides the target only when no Git root is found above the working directory. It needs exactly one candidate, so it resolves when `/workspaces` holds a single unambiguous repository and fails when it holds several. The Agent reads `.ona/mcp-config.json` at the start of each execution. Open a new conversation (or start a new Agent execution) after the devcontainer hooks finish. A full VM rebuild is not required. Confirm the environment is linked. Run `ultra doctor` inside the container: it reports link status and identity resolution. If the deploy key isn't picked up, verify `ULTRA_DEPLOY_KEY` is set in the Ona environment. ## Re-running setup manually Both commands are safe to re-run at any time: ```bash theme={null} ultra install --agent ona --yes ultra migrate --from ona --all --yes ``` Use this if you added new MCP connectors directly to `.ona/mcp-config.json` and want them pulled into Ultra's upstream config without rebuilding the environment. # CI and Automation Source: https://docs.ultra.security/installation/ci-setup Route agents running in CI pipelines through Ultra so their tool calls are traced and governed Agents that run in CI, such as PR review bots and pipeline automation, make MCP tool calls with no person at the keyboard. Running them through Ultra gives those calls the same traces, audit events, and governance rules as a developer laptop. This page uses GitHub Actions as the worked example. The same pattern applies to any CI system that can run a shell step and hold a secret. ## Before you start In Ultra Hub, create a workspace that holds only CI devices, separate from the ones your laptops and cloud dev environments link to. Policy and traces are scoped per workspace, so a CI-only workspace lets you govern pipeline agents without touching anyone's laptop. See [Workspaces](/hub/workspaces). Decide the address this pipeline's activity will be attributed to, and make sure it already exists as a member of the organization the deploy key belongs to. A device whose address does not resolve to a member stays ownerless, and its events are attributed to the machine rather than to that address. See [Identities](/hub/identities). Go to **Settings > Security** and create a key in the **Deploy Keys** section, scoped to that workspace. Interactive `ultra login` is not available in CI, so the deploy key is how the runner authenticates. Copy the `dk_...` value immediately, it is shown only once. See [Deploy keys](/hub/deploy-keys). Add the deploy key as `ULTRA_DEPLOY_KEY`. Add any connector credentials the agent needs as their own secrets. Note that GitHub rejects secret names beginning with `GITHUB_`. ## Quick start Add the install and connector steps to your workflow before the step that runs your agent: ```yaml theme={null} - name: Install Ultra and link to the ci workspace env: ULTRA_DEPLOY_KEY: ${{ secrets.ULTRA_DEPLOY_KEY }} ULTRA_DEVICE_EMAIL: ${{ vars.ULTRA_CI_IDENTITY }} run: | curl -sSL https://get.ultra.security/install.sh | bash ultra version - name: Register connectors env: GH_MCP_TOKEN: ${{ secrets.GH_MCP_TOKEN }} run: | ultra connectors add filesystem \ --command npx \ --args "-y @modelcontextprotocol/server-filesystem ${{ github.workspace }}" ultra connectors add github --url https://api.githubcopilot.com/mcp/ ultra config set-token github "$GH_MCP_TOKEN" - name: Confirm the device linked run: ultra doctor -v ``` Pipe the install script to `bash`, not `sh`. The script checks for bash and exits if it does not find it, and `/bin/sh` is not bash on Debian and Ubuntu, which covers most Linux CI runners and container images. To authenticate `install.sh` before it runs, replace the direct `curl` pipeline with the [verified install flow](/installation/install-ultra#verify-the-install-script-signature). Remote connectors authenticate with their own credentials, not with your CI system's built-in token. The remote GitHub MCP endpoint above needs a personal access token or a GitHub App token; a GitHub Actions `GITHUB_TOKEN` is not accepted. Give the pipeline a dedicated token with the narrowest scopes the agent needs, and fail the job early when it is missing rather than letting the agent fail partway through a run. Both connector forms work in CI without a prompt. `ultra connectors add` takes the transport and any environment variables as flags, and `ultra config set-token` takes the token as an argument, so nothing waits on input. Prefer that pair over the interactive `ultra add`, which prompts for credentials during setup. The runner is a fresh machine on every run, so it installs Ultra, links a device, and registers its connectors each time. Each run appears in Hub as a device in the workspace the deploy key is scoped to. ## Pointing the agent at Ultra A CI agent usually reads an MCP config from a path you control rather than from a user config directory. Commit a config that names Ultra as the only MCP server: ```json theme={null} { "mcpServers": { "ultra": { "command": "ultra", "args": ["start"] } } } ``` Then point your agent at that file. For Claude Code, pass it with `--mcp-config`, and add `--strict-mcp-config` so no other MCP configuration on the runner is picked up. For agents that read a config path Ultra already knows, `ultra install --agent --yes` writes the entry for you. See [Configure agents](/installation/configure-agents) for the supported list and their paths. ## An agent with other tools can route around Ultra Ultra governs MCP tool calls. An agent that reaches the same service another way, for example by calling `gh` or `curl` from a shell tool, or by reading the checkout with a built-in file tool, produces no MCP traffic, and Ultra has nothing to trace or govern. This matters more in CI than on a laptop, because CI agents are often written to shell out. If you want the traces and the governance rules to reflect what the agent actually did, the agent needs the connector and no alternate route. Check what your agent's flags actually do before relying on them. In Claude Code, `--allowed-tools` only pre-approves matching tools so they run without a prompt. It does not restrict what is available. Restricting the available tools is a different flag, and removing a tool from the agent's context entirely is `--disallowed-tools`. An agent configured with only an allow-list still has its built-in shell and file tools. ```yaml theme={null} claude_args: >- --mcp-config .github/ultra-mcp.json --strict-mcp-config --tools "" --disallowed-tools "Bash" --allowed-tools "mcp__ultra" ``` Two different controls are at work here, and it is worth keeping them apart: * **Which tools the agent has at all** is your agent's own configuration, not Ultra. Nothing in Ultra prevents someone adding a shell tool back later. * **Which of Ultra's tools the agent may actually call** belongs in a [governance](/hub/governance) rule scoped to the CI workspace. That is the half that survives a change to the workflow file, and it is the half Ultra enforces. Relying on the agent's flags alone gives you a configuration promise. Pairing them with a governance rule gives you an enforced one. ## Identity There is no person behind a CI run, so set the identity explicitly. Keep the value in a CI variable rather than hardcoding it in the workflow file: ```yaml theme={null} env: ULTRA_DEVICE_EMAIL: ${{ vars.ULTRA_CI_IDENTITY }} ``` Set `ULTRA_CI_IDENTITY` to an address you reserve for that pipeline, and use a distinct address per pipeline rather than a shared one, so a noisy or misbehaving agent can be told apart from the rest of your automation. The address only attributes activity when it resolves to an existing member of the deploy key's organization. If it does not resolve, the device is left without an owner, its identity status is reported as unresolved, and its events are attributed to the machine rather than to the address you set. Provision the member or service identity before the first run, and check the device in Hub afterwards rather than assuming attribution worked. ## Moving from monitor to enforcement CI is usually the first surface where enforcement is worth turning on. The agents are non-interactive, their tool use is narrow and repeatable, and nobody is blocked waiting on an approval. Leave the workspace on its inherited posture and let real pipeline runs build the tool catalog. You need to see what the agent actually calls before you can allowlist it. On the [Governance](/hub/governance) page, scope rules to the CI workspace and allow the connectors and tools the pipeline needs. This is also where you narrow a broad connector down to the operations the agent should be making, rather than leaving every tool it exposes reachable. With the allow rules in place, anything the pipeline was not approved for is denied at the device before it reaches the upstream connector, and the denial is recorded in the [audit log](/observability/audit-log). A denied call surfaces to the agent as a failed tool call. Whether the job then fails, retries, or carries on depends on how your agent handles tool errors, so run at least one deliberate deny against a non-blocking pipeline before you rely on this in a required check. ## Verification ```bash theme={null} # Ultra installed on the runner ultra version # Device linked, identity resolved, connectors reachable ultra doctor -v # Connectors registered for this run ultra connectors ``` In Hub, the run appears as a device in the CI workspace, and traces appear within about 60 seconds of the agent's first MCP tool call. A green check is not the signal to trust here. The trace list is. A job can pass having done nothing, so confirm the workspace filled before concluding the pipeline is wired correctly. ### Make the job prove the agent worked Build that check into the pipeline rather than leaving it to habit. Have the agent emit something the job can look for, such as a marker line in the comment it posts, and add a final step that fails when the marker is absent. Otherwise a denied tool call leaves the agent exiting cleanly and the job going green with no work done. The marker has to be unique to the run. A fixed string is satisfied by anything that happens to contain it, including an earlier comment, a template, or someone describing how the check works, so the job passes without the agent having done anything. Build the run identifier into the marker and match it exactly. ```yaml theme={null} env: REVIEW_MARKER: ``` ```bash theme={null} # ...then, after the agent step gh api "repos/${REPO}/issues/${PR_NUMBER}/comments" --paginate --jq '.[].body' \ | grep -qF "$REVIEW_MARKER" || { echo "::error::The agent did not post. Check the workspace traces." exit 1 } ``` A verification step that can be satisfied by something other than the work is decorative. It is worth testing it against a run you know did nothing, so you find that out deliberately rather than the first time it matters. ## Troubleshooting The script was piped to `sh`. On Debian and Ubuntu, including the standard Linux CI runners, `/bin/sh` is dash rather than bash. Pipe to `bash` instead. Every later step then fails with `ultra: command not found`, which is a symptom of this rather than a separate problem. Some agent actions refuse to run when the workflow file on the branch differs from the copy on your default branch, a guard against a pull request modifying the workflow that reviews it. The agent step is skipped and the job still succeeds, so the check goes green while nothing was reviewed. Expect this on the pull request that first adds the workflow, and treat it as resolved once the file is merged to the default branch. It is also a reason not to make a run like this a required check without also checking what it produced. Confirm `ULTRA_DEPLOY_KEY` is set on the step that runs the install script, and that the key has not been revoked. `ultra doctor -v` reports link status and identity resolution. The address did not resolve to a member of the deploy key's organization, so the device has no owner and its events are machine-attributed. Provision that identity in the organization, then re-run. `ultra doctor -v` reports identity resolution on the runner. A deploy key carries its own workspace, so the device links wherever the key points. Check which workspace the key was created under rather than looking for a workspace setting on the runner. The agent is most likely not calling MCP at all. Confirm it is reading the config that names Ultra, and that it still has a shell or file tool it can reach the same service with. An allow-list alone does not remove those. Confirm the token was set for the connector name you registered, since `ultra config set-token` takes that name as its first argument. Check that the token itself carries the scopes the connector needs, and that it is a credential the upstream accepts. A CI system's built-in token is often not one. Some CI systems run bot-authored builds against a separate secret store. On GitHub Actions, Dependabot runs do not see Actions secrets, so guard the job rather than letting it fail at the install step. # Configure Agents Source: https://docs.ultra.security/installation/configure-agents Set up your MCP agents to use Ultra Ultra works with **Claude Desktop**, **Cursor**, **VS Code** (including **GitHub Copilot**), **Cline**, **Claude Code**, **Codex**, **Grok Build**, **Windsurf**, **Roo Code**, **Goose**, **OpenCode**, **Gemini CLI**, **Antigravity**, **LM Studio**, **Amazon Q Developer**, **Zed**, **Ona**, and **Hermes**. After installing Ultra, configure your AI agents to route MCP traffic through it. For Ona cloud dev environments, see the dedicated Ona setup guide within [Agent Environments](/installation/agent-environments), which covers devcontainer-based automated installation. ## Automatic Configuration When you install Ultra using the downloaded installer, the **Ultra Setup** wizard launches automatically. It detects your installed AI agents and lets you select which ones to configure. On Windows, the wizard runs when you leave **Set up my MCP clients for Ultra now** checked on the installer's last screen. On a first install, every detected agent starts selected and locked, because **Automatically configure all AI apps** is checked. To choose agents yourself, uncheck that box, check the agents you want, and click **Configure**. Ultra adds itself to each selected agent's MCP configuration. Connectors the agent already had stay as they are; to route those through Ultra too, see [migrating existing connectors](/cli/migrate). After setup: 1. **Restart your AI agent** (e.g., quit and reopen Claude Desktop) 2. Start using your AI tools as usual To add or reconfigure agents later, see the **Terminal** tab. ### Automatically configure new agents The **Automatically configure all AI apps** checkbox ("Includes apps you install later.") decides whether Ultra keeps itself installed in every supported agent it detects going forward, including ones you install after this setup finishes. It does this without touching an agent's other MCP connectors or taking ownership of its config. * **Checked** (the default on a first install): every detected agent is selected and locked. Click **Configure** to set up all of them and turn on automatic setup for agents you add later. * **Unchecked**: you pick agents individually. Ultra configures only the ones you select and does not set up agents you add later. On an upgrade, or on a machine where Ultra was installed before, the box starts in your current setting: checked if automatic setup is on, unchecked otherwise. Clicking **Skip** leaves the setting unchanged. If enforcement is set to one of the advanced modes, or to a value the installer doesn't recognize or can't read, the wizard shows a short note in place of the checkbox and leaves the setting unchanged. To change the setting later, see **Automatically configure new agents** in the **Terminal** tab. If your organization sets drift enforcement in Ultra Hub, its value wins over this setting. Once connected, you can manage, discover, and reconnect your MCP connectors visually with the [`ultra_servers` MCP App](/getting-started/mcp-app), right inside your AI agent. ## Interactive Wizard Run the install wizard to be guided through the process: ```bash theme={null} ultra install ``` The wizard detects installed MCP agents, lets you choose which ones to configure, and sets Ultra as the MCP server in each agent's configuration. Run it any time to add or reconfigure agents. On Windows, open PowerShell from the Start menu to run it. Running the same Windows installer again does not reopen the setup wizard; on macOS, running the installer package again does. ## Automatically configure new agents This is the same setting as the wizard's **Automatically configure all AI apps** checkbox: ```bash theme={null} # Equivalent to checking the box ultra config set drift-enforcement auto_install # Equivalent to unchecking it ultra config set drift-enforcement default # Check the current value ultra config get drift-enforcement ``` The new value takes effect the next time Ultra starts, so **restart your AI agents** after changing it. If your organization sets drift enforcement in Ultra Hub, its value wins. See [`ultra config`](/cli/config#drift-enforcement) for details. ## Non-Interactive Mode Install into a specific agent: ```bash theme={null} # Claude Desktop ultra install --agent claude # Cursor ultra install --agent cursor # VS Code (native MCP) ultra install --agent vscode # Cline (VS Code extension) ultra install --agent cline # Claude Code ultra install --agent claude_code # Windsurf ultra install --agent windsurf # Goose ultra install --agent goose # Codex ultra install --agent codex # Grok Build ultra install --agent grok # Gemini CLI ultra install --agent gemini # Antigravity ultra install --agent antigravity # Roo Code ultra install --agent roocode # Amazon Q Developer ultra install --agent amazonq # LM Studio ultra install --agent lmstudio # Zed ultra install --agent zed # OpenCode ultra install --agent opencode # Hermes ultra install --agent hermes # Skip confirmation ultra install --agent claude --yes ``` Ona uses a workspace-scoped devcontainer flow rather than a per-machine install. See the [Ona setup guide](/installation/ona-setup) for the canonical setup. Install into all detected agents: ```bash theme={null} ultra install --all ``` ## List Detected Agents Preview which agents Ultra can detect without making changes: ```bash theme={null} ultra install --list ``` This shows each agent's installation status, config path, and any existing MCP connector configurations. ## Flags | Flag | Short | Default | Description | | - | - | - | - | | `--agent` | | | Target agent (`claude`, `cursor`, `vscode`, `cline`, `claude_code`, `windsurf`, `goose`, `codex`, `grok`, `opencode`, `gemini`, `antigravity`, `roocode`, `amazonq`, `lmstudio`, `zed`, `ona`, `hermes`) | | `--all` | | `false` | Install into all detected MCP agents | | `--config` | | | Path to Ultra config file | | `--no-backup` | | `false` | Skip backup of existing config | | `--yes` | `-y` | `false` | Skip confirmation prompt (used with `--agent`) | | `--list` | `-l` | `false` | List detected agents | Run `ultra doctor -v` to verify that Ultra is correctly installed and your MCP agents are properly configured. ## Supported Agents | Agent | Auto-Install | | - | :-: | | Claude Desktop | Yes | | Cursor | Yes | | VS Code (incl. GitHub Copilot) | Yes | | Cline | Yes | | Claude Code | Yes | | Windsurf | Yes | | Goose | Yes | | Codex | Yes | | Grok Build | Yes | | OpenCode | Yes | | Gemini CLI | Yes | | Antigravity | Yes | | Roo Code | Yes | | Amazon Q Developer | Yes | | LM Studio | Yes | | Zed | Yes | | Ona | Yes | | Hermes | Yes | All agents in the table are detected and configured automatically by `ultra install`. **GitHub Copilot** does not have a separate config file. It reads MCP connectors from VS Code's `mcp.json`, so running `ultra install --agent vscode` configures Ultra for both native VS Code MCP and Copilot. If the Copilot extension is detected, Ultra labels the agent as **VS Code + Copilot** in the setup wizard. ## What It Does When you configure an agent (either through the setup wizard or CLI), Ultra registers itself as an MCP server in the agent's configuration file. The agent then launches Ultra via `ultra start` whenever it needs MCP tools. Ultra in turn connects to your configured upstream connectors. Most agents use JSON with an `mcpServers` key: ```json theme={null} { "mcpServers": { "ultra": { "command": "/usr/local/bin/ultra", "args": ["start"] } } } ``` VS Code native MCP uses a `servers` key: ```json theme={null} { "servers": { "ultra": { "command": "/usr/local/bin/ultra", "args": ["start"] } } } ``` VS Code's `mcp.json` is JSONC, so `//` and `/* */` comments and trailing commas are all valid in it. Ultra reads and edits it as JSONC and patches only the `servers` key, so your comments, your formatting, and any fields Ultra does not model survive an install, a migration, or an uninstall. Connectors Ultra leaves in place are left byte-for-byte untouched. If the file cannot be parsed as JSONC, Ultra refuses to write rather than risk corrupting it. Goose uses YAML with an `extensions` key: ```yaml theme={null} extensions: ultra: cmd: /usr/local/bin/ultra args: - start type: stdio enabled: true ``` Codex uses TOML: ```toml theme={null} [mcp_servers.ultra] command = "/usr/local/bin/ultra" args = ["start"] ``` Grok Build uses the same TOML `mcp_servers` format as Codex, in `~/.grok/config.toml`: ```toml theme={null} [mcp_servers.ultra] command = "/usr/local/bin/ultra" args = ["start"] ``` Ultra overlays only the fields it models onto each connector entry, so Grok-specific keys such as `startup_timeout_sec`, `tool_timeout_sec`, and `bearer_token_env_var` are preserved. OpenCode uses JSON with an `mcp` key: ```json theme={null} { "mcp": { "ultra": { "command": "/usr/local/bin/ultra", "args": ["start"] } } } ``` Zed uses JSON with a `context_servers` key, embedded in the editor's main `settings.json`. Custom stdio connectors are marked with `"source": "custom"`: ```json theme={null} { "context_servers": { "ultra": { "source": "custom", "command": "/usr/local/bin/ultra", "args": ["start"] } } } ``` Zed's `settings.json` is JSONC, so it ships pre-populated with `//` comments and trailing commas. Ultra edits the `context_servers` key in place, preserving your other settings, comments, and any per-connector fields it does not model. If the file can't be parsed as JSONC, Ultra refuses to write rather than risk corrupting it. Hermes uses YAML with an `mcp_servers` key: ```yaml theme={null} mcp_servers: ultra: command: /usr/local/bin/ultra args: - start enabled: true ``` Antigravity uses the standard `mcpServers` JSON format. It intentionally stores its MCP config under the shared `.gemini` directory (in `config/mcp_config.json`), separate from the Gemini CLI's own `settings.json`. Ona uses the standard `mcpServers` JSON format, scoped to the workspace (the file lives inside the project checkout, not at a global path). ### Agent Config Paths | Agent | macOS | Windows | Linux | | - | - | - | - | | Claude Desktop | `~/Library/Application Support/Claude/claude_desktop_config.json` | `%APPDATA%\Claude\claude_desktop_config.json` | `~/.config/Claude/claude_desktop_config.json` | | Cursor | `~/.cursor/mcp.json` | `~/.cursor/mcp.json` | `~/.cursor/mcp.json` | | VS Code | `~/Library/Application Support/Code/User/mcp.json` | `%APPDATA%\Code\User\mcp.json` | `~/.config/Code/User/mcp.json` | | Cline | `~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json` | `%APPDATA%\Code\User\globalStorage\saoudrizwan.claude-dev\settings\cline_mcp_settings.json` | `~/.config/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json` | | Claude Code | `~/.claude.json` | `~/.claude.json` | `~/.claude.json` | | Windsurf | `~/.codeium/windsurf/mcp_config.json` | `~/.codeium/windsurf/mcp_config.json` | `~/.codeium/windsurf/mcp_config.json` | | Goose | `~/.config/goose/config.yaml` | `%APPDATA%\goose\config.yaml` | `~/.config/goose/config.yaml` | | Codex | `~/.codex/config.toml` | `~/.codex/config.toml` | `~/.codex/config.toml` | | Grok Build | `~/.grok/config.toml` | `~/.grok/config.toml` | `~/.grok/config.toml` | | OpenCode | `~/.config/opencode/opencode.json` | `%APPDATA%\opencode\opencode.json` | `~/.config/opencode/opencode.json` | | Gemini CLI | `~/.gemini/settings.json` | `~/.gemini/settings.json` | `~/.gemini/settings.json` | | Antigravity | `~/.gemini/config/mcp_config.json` | `%USERPROFILE%\.gemini\config\mcp_config.json` | `~/.gemini/config/mcp_config.json` | | Roo Code | `~/Library/Application Support/Code/User/globalStorage/rooveterinaryinc.roo-cline/settings/cline_mcp_settings.json` | `%APPDATA%\Code\User\globalStorage\rooveterinaryinc.roo-cline\settings\cline_mcp_settings.json` | `~/.config/Code/User/globalStorage/rooveterinaryinc.roo-cline/settings/cline_mcp_settings.json` | | Amazon Q Developer | `~/.aws/amazonq/mcp.json` | `~/.aws/amazonq/mcp.json` | `~/.aws/amazonq/mcp.json` | | LM Studio | `~/.lmstudio/mcp.json` | `~/.lmstudio/mcp.json` | `~/.lmstudio/mcp.json` | | Zed | `~/.config/zed/settings.json` | `%APPDATA%\Zed\settings.json` | `~/.config/zed/settings.json` | | Ona | `/.ona/mcp-config.json` | `\.ona\mcp-config.json` | `/.ona/mcp-config.json` | | Hermes | `~/.hermes/config.yaml` | `~/.hermes/config.yaml` | `~/.hermes/config.yaml` | Ultra stores its own configuration in `~/.config/ultra/config.yaml`. See [Configuration](/configuration/config-file) for details. ## After Configuration 1. **Restart your MCP agent** to load Ultra 2. Import existing MCP connectors with `ultra migrate` (see [Migrate Connectors](/installation/migrate-connectors)) 3. Or add new connectors with `ultra search` and `ultra add` # Install Ultra Source: https://docs.ultra.security/installation/install-ultra Install the Ultra binary on macOS, Linux, or Windows For enterprise fleet deployment via Jamf Pro, see the [MDM Deployment Guide](/installation/mdm-deployment). It covers zero-touch installation, deploy key authentication, and per-user identity resolution via SCIM. ## Download Ultra 1. [Request access](https://ultra.security/request-access). We'll email you when you're approved. 2. Sign in to [Ultra Hub](https://hub.ultra.security) and open **Settings > Downloads**. 3. Download and run the installer for your platform: `.pkg` on macOS, `.msi` on Windows. On Linux, extract the `.tar.gz` and move `ultra` into your PATH. ### Verify Installation After installing, open your AI agent and ask it to verify: * *"Check your connection to Ultra"* * *"Run ultra doctor"* Your agent should confirm that Ultra is installed and running. ## Install via Terminal Run the install script: ```bash theme={null} curl -sSL https://get.ultra.security/install.sh | sh ``` The install script: * Auto-detects your OS (macOS, Linux, Windows) and architecture (amd64, arm64) * Downloads from CDN (`get.ultra.security`) with GitHub Releases fallback * Installs to `/usr/local/bin` (or uses `sudo` if needed) * Removes macOS quarantine attributes automatically ### Environment Variables Customize the installation with environment variables: | Variable | Default | Description | | - | - | - | | `INSTALL_DIR` | `/usr/local/bin` | Custom installation directory | | `VERSION` | Latest release | Pin to a specific version | | `ULTRA_DEPLOY_KEY` | None | Deploy key to enroll with, saved during install | | `ULTRA_DEVICE_EMAIL` | None | Your work email, used to attribute this device to you | Set them on the `sh` that runs the script, not on `curl`, or the script never sees them: ```bash theme={null} # Install to a custom directory curl -sSL https://get.ultra.security/install.sh | INSTALL_DIR=~/.local/bin sh # Install a specific version curl -sSL https://get.ultra.security/install.sh | VERSION=0.5.0 sh ``` ### Verify Installation After installing, verify Ultra is working: ```bash theme={null} ultra version ``` Run diagnostics to check your system: ```bash theme={null} ultra doctor ``` ### Upgrading Re-run the install script to upgrade to the latest version: ```bash theme={null} curl -sSL https://get.ultra.security/install.sh | sh ``` ### Verify the Install Script Signature Stable releases include a detached signature for `install.sh`. Download the current [`v1` public key](https://get.ultra.security/install-signing-v1.pem) once and pin it in a location that only administrators can modify. The example below uses `/usr/local/share/ultra/install-signing-v1.pem`. Whenever you install or upgrade Ultra, download the current script and its matching signature: ```bash theme={null} curl -fsSLO https://get.ultra.security/install.sh curl -fsSLO https://get.ultra.security/install.sh.sig.v1 ``` Verify the signature with the pinned key, then run the installer: ```bash theme={null} openssl dgst -sha256 \ -verify /usr/local/share/ultra/install-signing-v1.pem \ -signature install.sh.sig.v1 \ install.sh && \ bash install.sh ``` OpenSSL prints `Verified OK` when the signature is valid. The signature proves that Ultra signed the downloaded bytes, but not that the script is the newest release. Do not run the installer if verification fails. ## Installing with a Deploy Key If your admin gave you a deploy key, hand it to the installer and your device links itself. No sign-in, and no `ultra login` or `ultra link`. The macOS and Windows installers both ask for the key during setup: macOS on a step titled **Ultra Enrollment**, Windows on the first step of **Ultra Setup**. 1. Paste the key into **Setup code**. Your admin may call it a deploy key; it is the same value and starts with `dk_` 2. Enter your **Work email**. This is what attributes the device to you in Ultra 3. Click **Continue** Choose **Skip** if you were not given a key. You can sign in through the browser instead the first time Ultra starts. Pass the key to the install script and it saves it for you. Set the variables on `sh`, not on `curl`: ```bash theme={null} curl -sSL https://get.ultra.security/install.sh | \ ULTRA_DEPLOY_KEY=dk_your_key_here \ ULTRA_DEVICE_EMAIL=you@example.com \ sh ``` Your device links to the key's workspace the first time Ultra starts. Already installed Ultra? Set the key afterwards and restart your AI agent, and Ultra picks it up on the next start. This reads the key from standard input, so it never lands in your shell history: ```bash theme={null} ultra config set deploy-key ``` See [Deploy Keys](/hub/deploy-keys) for the config file and managed preference options, plus the full reference. ## Platform Support | Platform | Architecture | Status | | - | - | - | | macOS | Intel (amd64) | Supported | | macOS | Apple Silicon (arm64) | Supported | | Linux | amd64 | Supported | | Linux | arm64 | Supported | | Windows | amd64 | Supported | After installing or upgrading Ultra, **restart your MCP agent** (e.g., quit and reopen Claude Desktop, Cursor, etc.) for changes to take effect. Ultra is distributed from a private repository. `go install` is not available. Use the installer from Ultra Hub or the install script. ## Next Steps Set up your MCP agents to use Ultra # MDM Deployment (Jamf) Source: https://docs.ultra.security/installation/mdm-deployment Deploy Ultra across your fleet via Jamf Pro for zero-touch enterprise setup Deploy Ultra across your organization using Jamf Pro. When deployed via MDM, Ultra automatically configures all detected MCP agents, migrates existing connector configurations, authenticates with Ultra Hub, and attributes each device to the correct employee via SCIM identity resolution. No user interaction required. ## Prerequisites Before deploying Ultra via MDM, ensure these are configured: * **Jamf Pro** with macOS device management * **Ultra Hub** organization with at least one workspace * **SSO** configured for your organization ([setup guide](/hub/sso)) * **SCIM** directory sync provisioning users from your IdP ([setup guide](/hub/scim)) * **Deploy key** created in Ultra Hub ([setup guide](/hub/deploy-keys)) SCIM provisioning is required for per-user identity resolution. Without it, devices are attributed to the admin who created the deploy key. SSO ensures users can authenticate independently if needed. ## Setup Follow the [Deploy Keys](/hub/deploy-keys) guide to create a key scoped to the workspace your fleet should link to. Copy the `dk_...` key immediately -- it is shown only once. Download the latest signed and notarized `.pkg`: ``` https://get.ultra.security/latest/Ultra-macOS.pkg ``` To pin a specific version: ``` https://get.ultra.security/v{VERSION}/Ultra-{VERSION}-macOS.pkg ``` In Jamf Pro, go to **Settings > Packages** and click **New** to upload the `.pkg`. Download the sample Configuration Profile: ``` https://get.ultra.security/Ultra-macOS.mobileconfig ``` In Jamf Pro, go to **Configuration Profiles > New** and upload the `.mobileconfig` file. You can use either upload method: * **Application & Custom Settings** — select the payload type and upload the file * **Upload** (raw mobileconfig) — upload the `.mobileconfig` directly without selecting a payload type Both methods are fully supported. Customize the following keys: | Key | Value | Description | | - | - | - | | `AutoSetup` | `true` | Enables zero-touch setup | | `DeployKey` | `dk_...` (your key) | Headless Hub authentication | | `UserEmail` | `$EMAIL` | Per-device user identity from Jamf inventory | The `$EMAIL` variable is a Jamf payload variable that auto-populates with the device owner's email from Jamf inventory. This requires user-device affinity to be configured (e.g., via SSO login during enrollment). **Optional keys:** | Key | Value | Description | | - | - | - | | `HubURL` | URL | Hub URL override (default: `https://hub.ultra.security`) | | `DisableAutoUpdate` | `true` | Disable self-updates to manage versions via Jamf | | `UserExternalID` | string | IdP external ID as secondary identity signal | In Jamf Pro, go to **Policies > New**: 1. Add the Ultra package under **Packages** 2. Scope to your target Smart Group or computers 3. Set trigger to **Enrollment Complete** (recommended) or **Recurring Check-in** 4. Ensure the Configuration Profile is scoped to the same targets Ultra installs silently on target machines. No user interaction is required at any point. ## How It Works When a user is logged in at install time: ``` Jamf installs .pkg -> postinstall detects AutoSetup=true from Configuration Profile -> ultra install --all (configures all MCP agents) -> auto-links device to Hub via deploy key -> Hub resolves UserEmail against SCIM directory -> ultra migrate --all --yes (migrates existing connectors) -> device visible in Hub immediately ``` The device appears in the Hub dashboard as soon as installation completes, before the user opens any MCP agent. When the user later opens an agent, `ultra start` detects the existing link and begins syncing policies, telemetry, and guardrails. When the `.pkg` installs before any user has logged in (Automated Device Enrollment): ``` Jamf installs .pkg -> postinstall detects no user session -> installs one-shot LaunchAgent -> user logs in for the first time -> LaunchAgent runs ultra install + migrate -> auto-links device to Hub via deploy key -> LaunchAgent self-removes ``` The device links to Hub during the `ultra install --all` step, so it appears in the dashboard as soon as the LaunchAgent runs. ## Identity Resolution Ultra uses SCIM directory sync to attribute each device to the correct employee. The resolution chain: 1. **Jamf** populates `UserEmail` with the device owner's email via the `$EMAIL` payload variable 2. **Ultra** sends this email to Hub during device link and on every heartbeat 3. **Hub** matches the email against the SCIM-provisioned user directory 4. If matched, the device is attributed to that user for audit, policy, and RBAC ### Unresolved Identity If the email doesn't match a SCIM user (e.g., the user hasn't been provisioned yet, or the Jamf inventory is incomplete): * The device **still links and works normally** * It is flagged as **Unresolved Identity** in the Hub dashboard * On subsequent heartbeats, Ultra re-sends the device identity * When the match succeeds (e.g., after the user is provisioned in SCIM), the device **auto-resolves** without any manual intervention A small number of devices may have missing Jamf inventory data if users don't complete the enrollment setup script. They'll show as "Unresolved Identity" until the inventory is corrected or the user is provisioned. ## Managing Updates By default, Ultra checks for updates every 24 hours and self-updates. For environments where you control rollout timing: * Set `DisableAutoUpdate` to `true` in the Configuration Profile * Use Jamf **Patch Management** or a **Smart Group** (e.g., "Ultra version \< X.Y.Z") to deploy new `.pkg` versions on your schedule The `.pkg` at `https://get.ultra.security/latest/Ultra-macOS.pkg` always points to the current stable release. ## Configuration Profile Reference All keys use the `com.ultra-security.ultra` preference domain. | Key | Type | Default | Description | | - | - | - | - | | `AutoSetup` | Boolean | `false` | Zero-touch setup (auto-install agents + migrate connectors) | | `DeployKey` | String | - | Deploy key for headless Hub authentication | | `UserEmail` | String | - | Device user's email for SCIM identity resolution | | `UserExternalID` | String | - | IdP external ID (secondary identity signal) | | `HubURL` | String | `https://hub.ultra.security` | Hub URL override | | `DisableAutoUpdate` | Boolean | `false` | Disable self-updates | ## Verifying Deployment On a managed machine: ```bash theme={null} # Check Ultra is installed ultra version # Check managed preferences are applied defaults read com.ultra-security.ultra # Check which agents are configured ultra install --list # Check postinstall log cat /tmp/ultra-postinstall.log ``` In the Ultra Hub dashboard: * Navigate to **Devices** to see everything that's been deployed * Each device should show the assigned employee (from SCIM) * Devices with missing identity data show an **Unresolved Identity** badge ## Troubleshooting **Ultra not in agent configs after install:** * Check `/tmp/ultra-postinstall.log` for errors * Verify the Configuration Profile is installed: `profiles show -type configuration` * Run `ultra install --all` manually to configure agents **Device shows "Unresolved Identity":** * Verify the user is provisioned in your IdP and synced via SCIM * Check that Jamf has the correct email in the device inventory (User & Location) * The device will auto-resolve on the next heartbeat once the email matches a SCIM user **LaunchAgent did not run on first login:** * Check if plist exists: `ls /Library/LaunchAgents/com.ultra-security.ultra-setup.plist` * Check agent log: `cat /tmp/ultra-setup-agent.log` * Run manually: `ultra install --all && ultra migrate --all --yes` **Deploy key authentication fails:** * Verify the key hasn't been revoked in Hub dashboard (Settings > Security) * Check the key hasn't expired * Ensure `DeployKey` is set correctly in the Configuration Profile: `defaults read com.ultra-security.ultra DeployKey` **Managed preferences not picked up:** * Verify the profile is deployed: `defaults read com.ultra-security.ultra` * If `defaults read` returns nothing but the profile is installed, check the plist file directly: ```bash theme={null} /usr/libexec/PlistBuddy -c "Print" "/Library/Managed Preferences/com.ultra-security.ultra.plist" ``` * Ultra reads managed preferences at startup. Restart the MCP agent to pick up changes. * Ultra supports multiple Jamf profile structures automatically, including raw mobileconfig uploads where keys are nested under `PayloadContent`. # Migrate Connectors Source: https://docs.ultra.security/installation/migrate-connectors Import existing MCP connectors into Ultra If you already have MCP connectors configured in your agents (Claude Desktop, Cursor, Codex, etc.), Ultra can automatically detect and import them so all your existing tools continue to work — now with full observability. ## Migrate Through Your Agent Ask your agent to migrate your existing MCP connectors into Ultra: * *"Migrate my existing MCP connectors to Ultra"* * *"Import my MCP connectors into Ultra"* Your agent will scan for any MCP connectors you already have configured, show you what it found, and import them into Ultra. ### What happens during migration 1. Ultra scans your agents for any MCP connectors you've already set up 2. Duplicate connectors (configured in multiple agents) are merged automatically 3. Your agent configs are updated to route through Ultra instead of connecting directly 4. All your existing tools continue to work exactly as before After migration, **restart your agent** for changes to take effect. To verify everything is working, ask your agent: * *"Check my Ultra connection"* * *"What connectors do I have connected?"* ## Interactive Wizard Run the migration wizard: ```bash theme={null} ultra migrate ``` The wizard scans your agents for configured connectors, shows what it found, and lets you choose which ones to import. ## Non-Interactive Mode ```bash theme={null} # Migrate all discovered connectors ultra migrate --yes # Migrate from a specific agent only ultra migrate --from claude --yes ``` ## Preview Changes See what would happen without making any changes: ```bash theme={null} # List discovered connectors ultra migrate --list # Dry run — preview the Ultra config that would be generated ultra migrate --dry-run ``` ## Flags | Flag | Short | Default | Description | | - | - | - | - | | `--config` | | | Path to Ultra config file | | `--yes` | `-y` | `false` | Skip confirmation prompt | | `--list` | `-l` | `false` | List discovered MCP connectors | | `--dry-run` | | `false` | Preview changes without applying | | `--from` | | | Migrate from specific agent only (`claude`, `cursor`, `codex`, `ona`, `hermes`, etc.) | ## After Migration 1. **Restart your agent(s)** to route traffic through Ultra 2. Run `ultra start` to start the proxy 3. Verify the migration was successful: ```bash theme={null} # Run diagnostics to check everything is connected ultra doctor -v # List your imported upstream connectors ultra connectors ``` 4. Run `ultra dashboard` to view traces # Introduction Source: https://docs.ultra.security/introduction Autonomous runtime security for AI agents **Agents take actions. Ultra secures them.** Agents aren't predictable, but security should be. Ultra sits between your agents and the tools they call, so every action is recorded, checked against your policy, and routed to the right connector. Built by security people, for security people. Guardrails and governance enforce policy inline on every tool call. Trace and audit every agent action, with alerts when something looks wrong. Connect each agent once and reach every connector through Ultra. ## Find Your Way Around Every feature has its own page. Start with the section that fits what you're doing. | Section | What's there | | - | - | | [Getting Started](/admin-setup) | Roll Ultra out and connect your agents | | [Ultra Hub](/hub/overview) | Devices, teams, workspaces, guardrails, and access | | [Observability](/observability/overview) | Traces, audit logs, alerts, and export | | [Concepts](/how-it-works) | How Ultra works | | [CLI Reference](/cli/overview) | Every `ultra` command | # Alerts Source: https://docs.ultra.security/observability/alerts A prioritized feed of high-signal security events across your organization Alerts are Ultra Hub's notifications. They gather the high-signal events from across your organization into one prioritized list, so you don't have to reconstruct what happened by reading the audit log. Open it from **Alerts** in the Hub sidebar, or go directly to `/alerts`. **Alerts are off until you switch them on.** A new organization raises no alerts at all. Nothing is missing from the feed and nothing is broken: no alert source is enabled yet. Turn on the sources you want from [Configuration](#configuration), below. ## Alert Sources Three sources can raise alerts. The **Type** column tells you which one produced a given alert, and each is switched on independently. | Type | What it alerts on | | - | - | | **Guardrail** | A guardrail blocked a request, redacted a response, or recorded a match in Monitor mode. | | **Governance** | An attempt to reach a connector your governance policy does not permit. | | **Anomaly** | A higher-risk finding from [anomaly detection](/observability/anomaly-detection). | ### Guardrail alerts Guardrail alerts cover what a guardrail actually did: blocked a request, redacted a response, or, in Monitor mode, allowed the request and recorded the match. The alert names the tool or connector involved. Alerting keys off the outcome Ultra recorded, not the mode you configured. A guardrail set to Block that let a call through recorded an allow, so it raises nothing. Monitor-mode guardrails do raise alerts, letting you watch the guardrail's real-world behavior before you set it to Block. See [Enforcement modes](/hub/guardrails#enforcement-modes). ### Governance alerts Governance alerts cover blocked attempts to reach a connector your governance policy does not permit. The alert names the blocked connector. ### Anomaly alerts Every anomaly finding carries a risk score between 0 and 1, shown on the finding itself. **A finding scoring 0.6 or above raises an alert**, so something worth acting on reaches the same feed as your enforcement events instead of waiting to be noticed on the Anomalies page. Findings below 0.6 are still detected, still recorded, and still shown in full on the [Anomalies page](/observability/anomaly-detection) with their scores — they just don't raise an alert. The threshold is fixed and the same for every organization, so a score you see on a finding tells you directly whether it alerted. Where a finding names exactly one resolvable user, the alert's context is that person. Otherwise it is recorded as organization-wide. Every enforcement event is recorded in the [audit log](/observability/audit-log) whether or not it raises an alert. To send alerts outside Ultra Hub, route them to [Slack](/observability/slack-alerts) or export them to your [SIEM](/observability/siem-export). ## Configuration Owners and admins choose what raises an alert from **Configuration** in the Alerts page header. You can turn alerts on or off for: * Each guardrail * Each governance block * Anomaly detection These settings change notifications only. Turning off a guardrail's alerts does not stop it from monitoring, blocking, or redacting. ## Test Alerts A test alert shows what a source produces, or confirms an integration is receiving alerts, without waiting for a real event. Owners and admins can click **Test** on any row in [Configuration](#configuration), then **Send test**. The test appears in the feed titled `Test:` followed by the source's name. It works even when the source is switched off, and it doesn't create traffic, traces, or audit events. To remove a test alert, open it and click **Delete test alert**. This removes it from Ultra only; a copy already sent to an integration stays there. ## Severity Ultra assigns severity, so an alert means the same thing across every organization. | Level | Meaning | | - | - | | **Critical** | Investigate immediately. | | **High** | Suspicious or persistent; review recommended. | | **Medium** | Unusual; worth a look. | | **Low** | Routine. | | **Informational** | Context only. | ## The Alerts Page Read state is per person. A teammate opening an alert doesn't clear it for you, and the unread count in the sidebar reflects only what *you* haven't opened. Opening an alert adds `?alert=` to the URL, so you can share a specific alert with a teammate. ### Exporting Export the feed as **CSV** or **JSON**. With nothing selected, the export covers the whole feed as currently filtered, not just the page you're looking at. With rows selected, it covers exactly those rows. Exports leave out read state, trigger details, and request context. ## Permissions | Capability | Owner | Admin | Member | Viewer | | - | - | - | - | - | | View the feed, detail, and exports | ✅ | ✅ | ✅ | ✅ | | Mark alerts read or unread, including **Mark all read** | ✅ | ✅ | ✅ | ✅ | | Change which sources alert | ✅ | ✅ | ❌ | ❌ | | Send and delete test alerts | ✅ | ✅ | ❌ | ❌ | See [RBAC](/hub/rbac) for the full permission matrix. ## Scope Alerts are **organization-scoped**: every member can see every alert, regardless of workspace membership. The workspace and device filters in the Hub header narrow the feed, its export, and **Mark all read**. Alerts that belong to no workspace or device, such as anomaly alerts, stay visible under any selection. # Anomaly Detection Source: https://docs.ultra.security/observability/anomaly-detection Scheduled, AI-driven security analysis of your organization's MCP traffic Ultra's anomaly detection runs as a background process in Ultra Hub that periodically scans your organization's MCP traffic, identifies suspicious patterns, and records findings in the dashboard and audit log. Detection is asynchronous and centralized. Individual devices do not evaluate calls inline, so there is no per-call latency cost. Configure detection from **Anomalies** in the Hub sidebar. ## How It Works Detection runs on a schedule you choose. Each scan analyzes the MCP traffic recorded since the previous scan, looks for suspicious patterns, and writes any findings into the dashboard and the audit log. Everything runs inside Ultra Hub. Your devices forward traces to the Hub as usual. There is no anomaly configuration in your device files, and nothing to deploy or maintain on the device side. ## Anomalies Page The **Anomalies** page (in the Hub sidebar) is the home for anomaly detection. It holds your detection settings, a summary of recent activity, a chart of findings, and a table you can switch between findings and scan runs. A **Run scan now** button sits in the page header. A time-range selector in the header (last hour, 24 hours, 7 days, or 30 days) scopes the summary tiles, the chart, and the table. ### Detection Settings The settings card controls whether detection runs and how often. Only owners and admins can change it. A status line shows whether scheduled scans are **Active** or **Paused**, along with when the setting was last updated. | Control | Options | Description | | - | - | - | | **Run scheduled scans** | On / Off | When enabled, Ultra scans your organization's traffic on the configured cadence and records findings. When off, no scheduled scans run and **Run scan now** is disabled. | | **Frequency** | 15m / 30m / 1h / 4h / 12h / 24h | How often Ultra re-scans your organization. Each run covers the time since the previous run. A shorter cadence surfaces issues sooner; a longer cadence reduces cost. | ### Run Scan Now The **Run scan now** button triggers an on-demand scan without waiting for the next scheduled run. A popover lets you pick how far back to look (the last 15 minutes, 1 hour, 4 hours, or 24 hours) before submitting. On-demand scans don't affect your scheduled cadence. The next scheduled scan still covers the full window since the last scheduled run. The button is disabled when: * You are not an owner or admin. * Scheduled scans are paused. * A scan is already running for your organization. You can start another scan as soon as the previous one finishes. ### Summary Tiles Four tiles summarize the selected time range: | Tile | What it shows | | - | - | | **Scans** | Scan runs completed in the range. | | **Events scanned** | Traces analyzed across those runs. | | **Findings** | Findings produced, with a breakdown of how many were critical and high. | | **Current risk** | A score from 0 to 1 summarizing recent findings. More severe and more recent findings raise it, and older ones gradually fade. | ### Anomaly Activity Chart The **Anomaly activity** chart shows every finding in the range as a dot. Its lane is the severity (Critical, High, Medium, Low), its position is when it was found, its color is the anomaly type, and its size reflects how many events it involved. Hover a dot for its risk score, event count, and age. Click it to open the finding. Click an entry in the type legend, or a severity lane label, to show only that type or severity. Active filters appear as chips with a **Clear filters** button, next to a `showing N of M` count. If the range holds only a few findings, a note under the chart offers to widen it. If findings can't be loaded, the page says so and offers **Retry** instead of showing an empty chart. ### Anomalies and Runs The table below the chart has two views, switched with **Anomalies** and **Runs** in its header. **Anomalies** is the default. It lists every finding in the range, newest first, with its type, when it was last seen, its risk, how many traces it involved, and the affected users. You can sort any column or group rows by anomaly type. Click a row to open the finding. **Runs** lists scan runs: when each ran, the window it covered, whether it was scheduled or on-demand, events analyzed, the run's risk score, the anomaly types it found, and its status. Click a completed run to see its findings. Runs can be exported as CSV or JSON. Each finding shows: * The **anomaly categories** that apply (a single finding may span several) * A **risk badge** and the **score**, **confidence**, and **recommended action** (block, alert, or allow) * A short **summary** of the finding * The **affected users** involved * The **affected traces**, which link to the trace in [Traffic](/observability/traces) Every finding a scan produces is listed with its score, including low and `None` findings. There is no score threshold to configure. Each finding opens in a drawer with its own shareable link. The page updates automatically while a scan is running. ## Alerts on Findings Anomaly findings can raise alerts in the [Alerts](/observability/alerts) feed and in any connected integrations. A [Slack channel](/observability/slack-alerts) receives an anomaly alert when the alert's severity matches that channel's filter. Alerting is off until an owner or admin turns on **Alert on detected anomalies** in **Alerts > Configuration**. [Anomaly alerts](/observability/alerts#anomaly-alerts) covers which findings raise an alert and who it is attributed to. An anomaly alert is titled **Anomalous activity** followed by the finding's anomaly types. ### Send a Test Alert To see what an anomaly alert looks like, or to confirm an integration receives it, send a test. Only owners and admins can. Open **Alerts**, then click **Configuration** in the page header. Under **Alert on detected anomalies**, click **Test** on the **Anomaly detection** row, then click **Send test**. Ultra adds an alert titled `Test: Anomaly detection` to the feed, with the context **Test**. It does not create a finding, traffic, traces, or audit events. Connected integrations receive a copy marked as a test. You can send a test while the switch is off. After sending one, wait a few seconds before sending another. To remove a test alert, open it and click **Delete test alert**. This removes it from Ultra only. A copy already delivered to an integration stays there. ## Anomaly Categories Findings are classified into the following categories, shown in the chart's type legend, the **Anomaly** column of the Anomalies view, and the **Types** column of the Runs view. A single finding can carry more than one category when activity matches a multi-stage pattern. | Category | What it covers | | - | - | | **Enumeration** | Systematic discovery of tools, connectors, or resources. | | **Privilege escalation** | Attempts to gain access or permissions beyond normal scope. | | **Data exfiltration** | Sensitive data read in bulk or sent to unauthorized destinations. | | **Injection** | Prompt or tool-call manipulation embedded in request payloads. | | **Credential abuse** | Credentials, tokens, or keys used or moved abnormally. | | **Evasion** | Activity that appears designed to avoid monitoring or controls. | | **Resource abuse** | Disproportionate or wasteful consumption of resources. | | **Behavioral anomaly** | Activity that deviates from an identity's established pattern. | ## Risk Levels Each finding carries a risk level that summarizes its severity: | Level | Description | | - | - | | **None** | No anomaly detected. | | **Low** | Routine activity; informational only. | | **Medium** | Slightly unusual; worth monitoring. | | **High** | Suspicious; review recommended. | | **Critical** | Likely malicious; investigate immediately. | The vast majority of findings on healthy traffic are **low** or **medium**. High and critical findings are rare and should prompt a review of the affected traces and identities. ## Permissions Only owners and admins can open the **Anomalies** page, change detection settings, or use **Run scan now**. Members and viewers don't see it in the sidebar. ## Configuration Reference Anomaly detection is configured per organization from the Hub UI. There is no equivalent in the device configuration file. A newly created organization starts with scheduled scans already turned on at a `24h` cadence. See [New Organization Defaults](/hub/guardrails#new-organization-defaults). Organizations that existed before this default was introduced are not backfilled; for them, and for any organization that never saved a setting, the values below are what the Anomalies page and the API report until an owner or admin makes a change. | Setting | Type | Default | Description | | - | - | - | - | | Run scheduled scans | bool | `false` | Master switch. When off, no scheduled or on-demand scans run. | | Frequency | enum | `24h` | One of `15m`, `30m`, `1h`, `4h`, `12h`, `24h`. | ## Next Steps Org-wide activity and recent findings at a glance Anomaly events alongside every other MCP operation Inline policy enforcement that complements scheduled detection Who can configure detection and trigger scans # Audit Log Source: https://docs.ultra.security/observability/audit-log Security audit events for compliance Ultra's audit interceptor records security-relevant events for every MCP operation. The audit log provides a compliance-ready trail of who did what, when, and whether it was allowed. ## Audit Event Structure Field names below are Ultra's stable wire schema and don't change with product terminology. See [Terminology](/hub/overview#hub-api) for how they map to Devices, Connectors, and Agents. | Field | Type | Description | | - | - | - | | `id` | string | Unique event identifier | | `trace_id` | string | Linked trace ID (if applicable) | | `event_type` | string | Event classification (see below) | | `severity` | string | `info`, `warning`, `error`, `critical` | | `upstream` | string | Upstream connector involved | | `action` | string | The tool/resource/prompt name or action | | `principal` | string | MCP Agent identity | | `outcome` | string | `allow`, `deny`, `alert`, `monitor`, `redact`, `error`, `success`, `failure`, `canceled` | | `reason` | string | Why (for deny/error outcomes) | | `details` | map | Flexible key-value context | | `timestamp` | datetime | When the event occurred | ### Multi-Tenant Fields | Field | Description | | - | - | | `workspace_id` | Workspace context | | `gateway_id` | Device that recorded the event | | `organization_id` | Organization context | | `user_id` | User who performed the action | | `user_email` | User's email address | | `user_name` | User's display name | | `user_role` | Organization role of the user (e.g. `owner`, `admin`, `member`) | | `user_member` | Whether the user is a current member of the organization. `true` for active members, `false` for observed non-members, omitted when membership has not been resolved (for example, in standalone devices) | | `user_assurance` | Strength of the identity attribution for this event — see [Identity attribution](#identity-attribution) | ### Identity attribution Each audit event carries an attribution tier indicating how confidently Ultra associated the activity with a specific user. The Hub dashboard renders this as a small coloured dot next to the user's name in the audit drawer and trace drawer. See the [Identities](/hub/identities) page for the full attribution model and how each identity rolls up across the dashboard. | `user_assurance` | Meaning | Dashboard indicator | | - | - | - | | `authenticated` | The event was performed by a logged-in user with an active session | Green dot | | `gateway` | The event was attributed via the device's registered owner rather than a session; typical for unauthenticated stdio traffic on a personal device. Shown in the dashboard as **Device-attributed** | Amber dot | | `none` | No identity could be attributed | Grey dot | When `user_member` is `false`, the dashboard also displays a **Non-member** badge next to the user's name. This indicates the user was observed in activity but is no longer a member of the organization — useful for spotting access by offboarded employees or external identities that were never granted membership. ## Event Types | Event Type | When It's Recorded | | - | - | | `tool_call` | An MCP tool was called | | `resource_read` | An MCP resource was read | | `prompt_get` | An MCP prompt was retrieved | | `policy_deny` | A policy denied a request | | `policy_allow` | A policy explicitly allowed a request | | `auth_failure` | Authentication failed | | `auth_success` | Authentication succeeded | | `config_change` | Configuration was modified | | `server_start` | Ultra server started | | `server_stop` | Ultra server stopped | | `upstream_connect` | Connected to an upstream connector | | `upstream_disconnect` | Disconnected from an upstream connector | | `guardrail` | A guardrail evaluated a request | | `governance` | A governance rule or default posture allowed or denied a request, or a governance posture was changed | | `token_refresh` | An authentication token was refreshed | | `config_drift` | An unmanaged connector was found in the configuration of an agent Ultra has already migrated | | `config_remediation` | A drifted connector was brought back under Ultra management, or the attempt to do so failed | | `error` | An error occurred | Drift events are recorded against the `system` principal rather than an MCP agent, with the affected connector in the `upstream` field. Drift counts are also surfaced per device in the Hub — see [Devices](/hub/devices). ## Authentication Context Events involving an upstream connector carry an `auth_method` field in `details`, recording how Ultra authenticated to that connector: | Auth Method | Description | | - | - | | `oauth` | OAuth 2.0, including tokens obtained through the browser authorization flow | | `pat` | A personal access token supplied in the `Authorization` header | | `env_credential` | Credentials passed to the connector through environment variables | | `none` | No authentication configured | This field appears on `tool_call`, `resource_read`, `prompt_get`, `auth_success`, `auth_failure`, and `token_refresh` events. Because `token_refresh` only applies to OAuth connectors, its `auth_method` is always `oauth`. `token_refresh` and OAuth `auth_success` events also carry a `refresh_source` field identifying which path produced the token, which is useful when tracing why a connector re-authenticated. Operation events additionally record `client_id` and `client_version` in `details`, mirroring the [client metadata](/observability/traces#client) on the corresponding trace. ## Severity Levels | Level | Description | Examples | | - | - | - | | `info` | Normal operations | Tool calls, resource reads | | `warning` | Potential issues | Auth token expiring, slow responses | | `error` | Failures | Upstream connection errors, tool failures | | `critical` | Security events | Auth failures, policy denials | ## Outcomes | Outcome | Description | | - | - | | `allow` | Operation was permitted | | `deny` | Operation was blocked | | `alert` | Guardrail matched and generated a warning, but allowed the request | | `monitor` | Guardrail matched silently for visibility only | | `redact` | Guardrail matched and masked sensitive content before forwarding | | `error` | Operation failed with an error | | `success` | Operation completed successfully | | `failure` | Operation did not complete | | `canceled` | Operation was canceled | ## Audit Completeness The audit layer is designed to block a successful operation if it can't be recorded. There is no way for an operation to succeed without being captured in the audit log. This is a deliberate security design choice. For compliance-sensitive deployments, it guarantees that every successful operation has a corresponding audit record. ## Querying Audit Events ### Dashboard The web dashboard provides an audit log viewer with filtering: ```bash theme={null} ultra dashboard ``` ### Query Options Audit events can be filtered by: * Event type, severity, outcome * Upstream connector * MCP Agent (principal) * User ID, organization ID * Time range * Workspace and device (for Hub deployments) Each event has a **Copy link** button to share a deep link to it, and the filtered list can be downloaded with the **Export** control as CSV or JSON. See [Exporting table data](/observability/dashboard#exporting-table-data) and [Sharing links](/observability/dashboard#sharing-links). # Dashboard Source: https://docs.ultra.security/observability/dashboard Web-based monitoring dashboard Ultra includes a built-in web dashboard for monitoring MCP traffic, inspecting traces, and viewing audit logs. ## Starting the Dashboard ```bash theme={null} ultra dashboard ``` This starts a web server at `http://localhost:8080` and displays activity recorded on your device. ## Flags | Flag | Short | Default | Description | | - | - | - | - | | `--address` | `-a` | `:8080` | Listen address for the dashboard | | `--dev` | | `false` | Enable development tools | ## Examples ```bash theme={null} # Default: port 8080 ultra dashboard # Custom port ultra dashboard --address :9090 ``` ## Dashboard Features ### Traffic Overview Real-time view of **recent** MCP operations flowing through Ultra: * Request count, success/error rates * Per-connector breakdown * Latency distribution ### Trace Inspector Drill into individual operations: * Full request/response payloads * Duration and status * Upstream connector and tool name * OpenTelemetry trace and span IDs ### Audit Log Viewer Browse security audit events: * Filter by event type, severity, and outcome * View details for each event * Track who did what and when ## Exporting table data Most data tables in the dashboard carry an **Export** control in the toolbar. It's available on Traffic, Audit, Connectors, Tools, and Agents in both the Hub dashboard and the local `ultra dashboard`, plus Devices, Identities, Anomaly scans, and the Admin Log, which are Hub-only surfaces. Click **Export** and choose **CSV** or **JSON**: * **The full result set, not just the page you're looking at.** Whatever filters, search, and sorting are applied carry into the file, and every matching row is included across pagination, up to a cap of 50,000 rows. Past that cap the export stops and a notice asks you to narrow the filters to pull the rest. * **Only the visible columns are exported.** Action and UI-only columns are skipped, and internal fields that some rows carry (raw request/response payloads, workspace and organization IDs) are never written to the file. * **CSV** is UTF-8 with a byte-order mark so spreadsheet apps render names and emails correctly, uses RFC 4180 line endings, and hardens against spreadsheet formula injection. **JSON** preserves native types (numbers stay numbers, booleans stay booleans) for programmatic use. * Files are named `-`, for example `audit-20260601-214233.csv`. ### Exporting a selection Tables with a checkbox column let you export a subset instead of the whole result set. Tick individual rows, or use the header checkbox to select every row on the current page, then choose **Export** to download only those rows. The toolbar shows the selected count with a **Clear** control. Selection is scoped to the page you're viewing and resets when you change filters, sorting, or page. On some pages, a selection also enables bulk actions next to **Export**. ## Sharing links Detail views in the dashboard are deep-linkable, so you can point a teammate straight at a specific record. | Surface | Link opens | | - | - | | Traffic | A specific trace (`?trace=`) | | Audit | A specific audit event (`?event=`) | | Agents | A specific agent (`/agents/`) | | Devices | A specific device (`?gateway=`) | | Anomalies | A specific finding (`?finding=`) | Traffic, Audit, Devices, and Anomalies open their detail as a drawer over the list. Use the **Copy link** button (the link icon in the drawer header) to copy a URL that reopens that exact item; opening the link loads the list page and automatically opens the matching drawer. If the target no longer exists (for example, a device that has since been removed), the dashboard shows a "not found" notice instead of opening an empty drawer. An agent's detail opens as a full page rather than a drawer, so the address bar itself is the shareable link; see [Agents](/hub/agents) for what the page shows. ## Monitoring Live Traffic You can run the dashboard while Ultra is running to monitor traffic in real time. Run `ultra start` in one terminal and `ultra dashboard` in another to monitor traffic in real time. # OTLP Export Source: https://docs.ultra.security/observability/otlp-export Export telemetry to external observability platforms Ultra supports exporting traces and metrics via the **OpenTelemetry Protocol (OTLP)** to external observability backends like Jaeger, Grafana Tempo, Datadog, and others. ## Configuration Enable OTLP export in your Ultra config: ```yaml theme={null} observability: enabled: true endpoint: "localhost:4317" # OTLP gRPC endpoint service_name: "ultra" # Service name in traces ``` | Field | Type | Default | Description | | - | - | - | - | | `enabled` | bool | `false` | Enable OTLP telemetry export | | `endpoint` | string | `""` | OTLP gRPC endpoint | | `service_name` | string | `"ultra"` | Service name for telemetry | ## Backend Examples ### Jaeger Run Jaeger locally and point Ultra to it: ```bash theme={null} # Start Jaeger with OTLP support docker run -d --name jaeger \ -p 4317:4317 \ -p 16686:16686 \ jaegertracing/all-in-one:latest ``` ```yaml theme={null} observability: enabled: true endpoint: "localhost:4317" ``` View traces at `http://localhost:16686`. ### Grafana Tempo ```yaml theme={null} observability: enabled: true endpoint: "tempo.example.com:4317" service_name: "ultra-production" ``` ### Datadog Use the Datadog OTLP ingest endpoint: ```yaml theme={null} observability: enabled: true endpoint: "localhost:4317" # Datadog Agent OTLP port ``` Ensure your Datadog Agent is configured with OTLP ingest enabled. ### Honeycomb ```yaml theme={null} observability: enabled: true endpoint: "api.honeycomb.io:443" ``` ## What Gets Exported * **Traces** — OpenTelemetry spans for every MCP operation with W3C Trace Context IDs * **Metrics** — Request counts, latency histograms, error rates ## Local Dashboard and Export Enabling OTLP export keeps your activity available in the local Ultra dashboard while sending telemetry to your chosen backend for alerts, SLOs, and cross-service correlation. # Observability Overview Source: https://docs.ultra.security/observability/overview How Ultra monitors and records MCP traffic Ultra provides complete observability over MCP traffic through a pipeline of interceptors. Every tool call, resource read, and prompt request is automatically traced, audited, logged, and measured. ## The Pipeline Every MCP request passes through Ultra's observability layers before reaching the upstream connector. Responses pass back through the same layers: ```mermaid theme={null} graph LR Agent["MCP Agent"] Pipeline["Pipeline
Trace · Audit · Logging · Metrics"] Connector["Upstream Connector"] Agent -->|"Request"| Pipeline Pipeline -->|"Request"| Connector Connector -->|"Response"| Pipeline Pipeline -->|"Response"| Agent ``` | Layer | What It Records | | - | - | | **Trace** | Distributed traces with request/response payloads and duration | | **Audit** | Security-relevant events with severity, outcome, and principal | | **Logging** | Structured log entries for every request/response | | **Metrics** | Request counters, latency histograms, error rates | The audit layer is the only one that will block a successful operation if it can't record — guaranteeing every completed MCP call has a corresponding audit record. The others absorb errors silently so observability never breaks your workflow. Enforcement runs in this same pipeline. When a device has local storage, governance and guardrail interceptors are appended after the observability layers, so a blocked request is still traced and audited before it is stopped. See [Guardrails](/hub/guardrails) and [Governance](/hub/governance). ## Where Data Lives Observability data is stored locally on your device by default. Trace records, audit events, and metrics all live on-device unless you enable Hub sync or OTLP export. ### Viewing Data ```bash theme={null} # Web dashboard ultra dashboard # Dashboard on custom port ultra dashboard --address :9090 ``` ### Hub Sync When connected to Ultra Hub, traces and audit events are automatically synced to the cloud for team-wide visibility. Local storage is always maintained regardless of Hub connectivity. ## What Gets Recorded ### MCP Operations | Operation Type | Triggered By | | - | - | | `tool_call` | AI agent calls an MCP tool | | `resource_read` | AI agent reads an MCP resource | | `prompt_get` | AI agent retrieves an MCP prompt | | `tool_list` | Agent lists available tools | | `resource_list` | Agent lists available resources | | `prompt_list` | Agent lists available prompts | ### For Every Operation * **Trace ID and Span ID** — OpenTelemetry-compatible identifiers for distributed tracing * **Upstream connector** — Which connector handled the request * **Tool/resource/prompt name** — What was called * **Request payload** — The arguments sent * **Response payload** — The result returned * **Duration** — How long the operation took * **Status** — Success or error * **User** — User identity (when connected to Ultra Hub) * **Principal** — The MCP agent identity * **Agent context** — Which MCP agent made the call, and its version * **Connector context** — Transport, process, and authentication method of the upstream connector Agent and connector context is recorded as trace metadata. See [Trace metadata](/observability/traces#trace-metadata) for the full set of keys and when each one is present. ## Next Steps Understand trace record structure Security audit events View data in the web dashboard Prioritized feed of high-signal security events AI-driven analysis of MCP traffic Export to Jaeger, Grafana, or Datadog Send alerts to Panther through S3 Send alerts to channels in your Slack workspace # SIEM Export (Panther) Source: https://docs.ultra.security/observability/siem-export Send Ultra alerts to Panther through S3 Ultra can deliver your organization's alerts to an S3 bucket in your AWS account. Your SIEM reads them from there. This guide connects that bucket to **Panther**, but any tool that reads newline-delimited JSON from S3 can use the same export. ## What You Get * Every alert Ultra raises, delivered as gzip-compressed NDJSON files (one alert per line) * Alerts raised from the moment you enable delivery. Earlier alerts are not backfilled. Ultra can only write to your bucket. It cannot read, list, or delete anything in it. ## Prerequisites * **Owner or admin** role in Ultra Hub * An **AWS account** where you can create an S3 bucket and an IAM role * A **Panther** account with permission to add log sources and schemas ## Connect Amazon S3 First, connect Ultra Hub to your S3 bucket. In Ultra Hub, go to **Settings > Integrations**. Under **Available integrations**, click **Add** next to **Panther**. Ultra walks you through the S3 connection and shows the Panther settings on the last step. For another SIEM that reads newline-delimited JSON from S3, click **Add** next to **Amazon S3** instead. The steps are the same, without the Panther step. Fill in: | Field | Value | | - | - | | **Connection name** | Any name, for example `Panther` | | **Destination name** | Any name for this bucket | | **Region** | The bucket's AWS region, for example `us-east-1` | | **Bucket** | Your S3 bucket name | | **Prefix** | The folder Ultra writes under, for example `exports/ultra` | | **Encryption** | **SSE-S3**, or **SSE-KMS** with a KMS key ARN in the same region | Click **Continue**. Ultra Hub shows two policies generated for your connection: * A **trust policy** that lets Ultra assume the role, using an external ID unique to your connection * A **permissions policy** that allows writing to your bucket and prefix only In AWS IAM, create a role with the trust policy and attach the permissions policy. Then click **I've created the role**. This role is only for Ultra's writes. Panther reads the bucket with its own role, which you create in [Connect Panther](#connect-panther). If you chose SSE-KMS and your key policy does not delegate access to IAM, also allow this role in the key policy. Paste the new role's ARN into **Customer role ARN** and review the summary. * From the **Panther** tile, click **Continue**. The **Set up Panther** step shows the Panther settings for your bucket and prefix. Follow them, or the steps in [Connect Panther](#connect-panther), then click **Finish setup**. * From the **Amazon S3** tile, click **Finish setup**. Ultra creates the connection with its alert stream **disabled**, so nothing is delivered until you enable it in the last step. If you leave before finishing, open the connection and click **Resume setup**. ## Connect Panther Next, point Panther at the same bucket, then turn on delivery. In Panther, go to **Schemas** and click **Create New**. Set: | Setting | Value | | - | - | | **Schema ID** | `Custom.Ultra.Alert.V1` | | **Description** | `Ultra Hub alerts delivered to S3` | | **Field Discovery** | Off | Under **Schema**, click **Start** on **Create your schema from scratch**, keep the **Default (JSON/XML)** parser, and paste this into **Fields & Indicators**: ```yaml theme={null} fields: - name: schema_version required: true type: bigint - name: record_type required: true type: string validate: allow: [alert] - name: record_id required: true type: string - name: observed_at required: true type: timestamp timeFormats: - rfc3339 isEventTime: true - name: exported_at required: true type: timestamp timeFormats: - rfc3339 - name: data required: true type: object fields: - name: organization_id required: true type: string - name: workspace_id required: false type: string - name: gateway_id required: false type: string - name: title required: true type: string - name: description required: true type: string - name: severity required: true type: string validate: allow: [critical, high, medium, low, informational] - name: source_type required: true type: string validate: allow: [guardrail, anomaly, policy, system, supply_chain, governance] - name: source_id required: false type: string - name: context_type required: false type: string - name: context_id required: false type: string - name: context_label required: false type: string - name: aggregate_count required: true type: bigint - name: first_seen_at required: true type: timestamp timeFormats: - rfc3339 - name: last_seen_at required: true type: timestamp timeFormats: - rfc3339 - name: recommended_action required: false type: string - name: enforcement_event_id required: false type: string - name: outcome required: false type: string - name: guardrail_slug required: false type: string - name: principal required: false type: string - name: user_id required: false type: string - name: user_email required: false type: string - name: method required: false type: string - name: trace_id required: false type: string - name: synthetic required: false type: boolean ``` Click **Run Test**, then **Save**. In Panther, go to **Configure > Log Sources**, click **Create New**, and choose **AWS S3 Bucket**. Enter the AWS account ID that owns the bucket, the bucket name, and your KMS key ARN if you use SSE-KMS, then set: | Setting | Value | | - | - | | **S3 prefix** | `/ultra/v1/` | | **Schema** | `Custom.Ultra.Alert.V1` | | **Stream type** | `Lines` | Use the CloudFormation or Terraform template Panther provides to create its read-only role and the bucket notifications. See [Panther's S3 source guide](https://docs.panther.com/data-onboarding/data-transports/aws/s3) for details. If you use SSE-KMS, give Panther's read-only role `kms:Decrypt` and `kms:DescribeKey` on the same key, including in the key policy. Don't add decrypt access to the role Ultra writes with. In Ultra Hub, open your connection under **Settings > Integrations**, expand the destination, and click **Enable** on its alert stream. To confirm everything works, open **Configuration** from the Alerts page and click **Test** on any alert source. The test alert appears in Panther with `synthetic` set to `true`. ## Alert Fields Each line in an exported file is one alert: | Field | Description | | - | - | | `record_id` | Alert ID | | `observed_at` | When the alert was raised. Panther uses this as the event time. | | `exported_at` | When Ultra delivered the file | | `synthetic` | `true` for test alerts sent from Alerts configuration | | `data.title`, `data.description` | Alert title and description | | `data.severity` | `critical`, `high`, `medium`, `low`, or `informational` | | `data.source_type` | `guardrail`, `anomaly`, `policy`, `system`, `supply_chain`, or `governance` | | `data.workspace_id`, `data.gateway_id` | Workspace and device the alert came from | | `data.context_type`, `data.context_id`, `data.context_label` | The connector, tool, device, or user the alert is about | | `data.aggregate_count`, `data.first_seen_at`, `data.last_seen_at` | How many times it occurred, and when | | `data.recommended_action` | Suggested next step | | `data.outcome`, `data.guardrail_slug`, `data.method` | What was enforced, by which guardrail, on which MCP method | | `data.principal`, `data.user_id`, `data.user_email` | The agent and user involved | | `data.trace_id`, `data.enforcement_event_id` | Links to the matching trace and audit log event | Optional fields are left out when empty, but `synthetic` is always present as `true` or `false`. Values are exported as-is, including user emails. ## Manage the Connection Open the connection from **Settings > Integrations**. * **Pause delivery**: click **Disable** on the destination's **Alert stream**. Deliveries already accepted still finish. Alerts raised while the stream is disabled are not sent when you enable it again. * **Delivery history**: in the stream's menu, choose **History**. * **Delete a destination**: in the destination's menu, choose **Delete destination**. Ultra then shows a replacement permissions policy under **Update AWS permissions**. Apply it to your AWS role, then click **Done**. * **Delete the connection**: in the connection's menu, choose **Delete connection**. This also deletes its destinations, streams, and delivery history. Ultra can't change your AWS role, so remove the trust and permissions policies from it yourself. ## See Also * [Alerts](/observability/alerts) * [Slack Alerts](/observability/slack-alerts) * [Audit Log](/observability/audit-log) * [OTLP Export](/observability/otlp-export) # Slack Alerts Source: https://docs.ultra.security/observability/slack-alerts Send Ultra alerts to channels in your Slack workspace Ultra can post your organization's alerts to Slack channels, so your teams see them where they already work. You choose which channels receive alerts and which severities each channel gets. ## What You Get * Alerts posted to any Slack channel the Ultra app has been invited to, public or private * A severity filter per channel, so a high-traffic channel only gets what matters * A link on every message that opens the alert in Ultra Hub ## Prerequisites * **Owner or admin** role in Ultra Hub * Permission to install apps in your Slack workspace * At least one alert source switched on in [Alerts configuration](/observability/alerts#configuration). A channel only receives alerts from sources that are switched on. ## Set Up Slack Alerts In Ultra Hub, go to **Settings > Integrations**. Under **Available integrations**, click **Add** next to **Slack**. Available integrations in Ultra Hub, with Add next to Slack Slack asks you to approve the Ultra app for your workspace. Ultra requests only these permissions: | Permission | Why Ultra needs it | | - | - | | `channels:read` | List the public channels the app has been invited to | | `groups:read` | List the private channels the app has been invited to | | `chat:write` | Post alerts to those channels | | `channels:manage` | Leave a public channel when you remove it from Ultra | | `groups:write` | Leave a private channel when you remove it from Ultra | Ultra cannot read messages in your workspace. After you approve, you return to **Settings > Integrations** and your workspace appears under **Connections**. A Slack workspace can be connected to only one Ultra organization. In Slack, open the channel you want alerts in. Open the channel details, go to **Members**, click **Add people or agents**, search for **Ultra**, and add the app. You can also type `@Ultra` in the channel and click **Add Them** when Slack offers to invite it. For a private channel, the app must be invited the same way. Ultra never sees a private channel it has not been invited to. Back in Ultra Hub, open your Slack connection under **Settings > Integrations** and click **Refresh channels**. The channel now appears under **Channel routing**. A channel is listed only after the Ultra app has been invited to it, so if you don't see yours, check that the app is a member and refresh again. Repeat the invite for as many channels as you like. Each one is routed separately. Channel routing listing two Slack channels, with the tooltip explaining that the Ultra app must be invited first Click **...** on the channel's row and choose **Enable alerts**. The row changes to **Alerts on**. By default a channel receives **Critical** and **High** alerts. To change that, click **...** and choose **Edit severities**. Under **Send alerts for**, check the severities you want (Critical, High, Medium, Low, or Informational) and click **Apply**. The selected severities are listed under the channel name. A newly enabled channel shows as **Unverified** until Ultra delivers its first alert to it. To confirm delivery now instead of waiting for a real alert, send a test: 1. Go to **Alerts** and click **Configuration** in the page header. 2. Click **Test** on any alert source, then **Send test**. The test alert appears in your channel within a few seconds, titled `Test:` followed by the source name, with the context `Test`. Once it is delivered, the connection and the channel both change to **Healthy**. The test alert has to match the channel's severity filter to be posted. A test is sent at **High** for a guardrail in Block mode, a governance block rule, or anomaly detection while it is on, so it reaches a channel on the default filter. A test for a guardrail in Redact or Monitor mode is sent at Medium or Low, and will not reach a channel filtered to Critical and High. ## Alert Messages A single alert is posted as a card with the alert title, **Severity**, **Source**, **Context**, and **Observed** time, plus a **View alert in Ultra** button that opens that alert in Ultra Hub. The time shows in each reader's own timezone. A single Ultra alert posted in Slack When several alerts arrive within a few seconds of each other, Ultra groups them into one message instead of posting each separately. The message is titled with the number of alerts (for example **3 Ultra alerts**), shows a count per severity, lists up to five alerts, and has a **View all in Ultra** button. Three Ultra alerts grouped into one Slack message Messages never include the raw content that triggered a guardrail. See [Alerts](/observability/alerts) for what each alert contains. ## Health Status Your Slack connection and each channel with alerts on show a health status. | Status | Meaning | | - | - | | **Unverified** | Ultra has not delivered an alert yet. [Send a test alert](#set-up-slack-alerts) to verify it. | | **Healthy** | The most recent delivery succeeded. | | **Degraded** | Deliveries are failing temporarily. Ultra keeps retrying. | | **Authorization required** | Ultra's access to the workspace was revoked or disconnected. Reconnect the workspace. | | **Misconfigured** | Ultra cannot post to the channel, for example because the app was removed or the channel was archived. The error is shown on the channel row. | ## Change or Remove a Channel * **Change severities:** click **...** on the channel and choose **Edit severities**. * **Stop alerts:** click **...** on the channel and choose **Disable alerts**. Alerts queued for that channel are dropped, and alerts raised while it is off are not sent later. * **Remove a channel:** disable its alerts in Ultra Hub first, then remove the Ultra app from the channel in Slack. ## Disconnect Slack Click **...** on the connection card and choose one of: * **Disconnect workspace:** turns off alerts for every channel, drops any queued alerts, and revokes Ultra's access to Slack. The connection stays in Ultra Hub so you can reconnect it later. After reconnecting, turn alerts back on for each channel. * **Delete connection:** does the same, then removes the connection from Ultra Hub. This cannot be undone. To confirm, type `Delete` followed by the connection name (for example `Delete Ultra`) and click **Delete Connection**. ## See Also * [Alerts](/observability/alerts) * [SIEM Export (Panther)](/observability/siem-export) * [Audit Log](/observability/audit-log) # Traces Source: https://docs.ultra.security/observability/traces MCP operation trace records Ultra records a **trace record** for every MCP operation. Traces capture the full lifecycle of a request — what was called, what arguments were sent, what came back, and how long it took. ## Trace Record Structure Field and metadata key names below are Ultra's stable wire schema and don't change with product terminology. See [Terminology](/hub/overview#hub-api) for how they map to Devices, Connectors, and Agents. Each trace record contains: | Field | Type | Description | | - | - | - | | `id` | string | Unique record identifier | | `trace_id` | string | OpenTelemetry trace ID (shared across related operations) | | `span_id` | string | OpenTelemetry span ID (unique to this operation) | | `parent_span_id` | string | Parent span for nested operations | | `operation` | string | Operation type (see below) | | `upstream` | string | Name of the upstream connector that handled the request | | `name` | string | Tool, resource, or prompt name | | `request` | JSON | Full request payload | | `response` | JSON | Full response payload | | `status` | string | `"success"` or `"error"` | | `error` | string | Error message (if status is error) | | `duration` | duration | How long the operation took (nanoseconds) | | `timestamp` | datetime | When the operation occurred | | `metadata` | map | Additional key-value context (see [Trace metadata](#trace-metadata)) | ### Multi-Tenant Fields When connected to Ultra Hub, traces also include: | Field | Description | | - | - | | `workspace_id` | Workspace this device belongs to | | `gateway_id` | This device's unique identifier | ## Operation Types | Type | Description | | - | - | | `tool_call` | An MCP tool was invoked | | `resource_read` | An MCP resource was read | | `prompt_get` | An MCP prompt was retrieved | | `tool_list` | Agent listed available tools | | `resource_list` | Agent listed available resources | | `prompt_list` | Agent listed available prompts | ## Trace Metadata The `metadata` map carries context that does not have a dedicated column. Keys are only present when Ultra has a value for them, so treat every key below as optional rather than guaranteed. ### User Populated when the device is linked to Ultra Hub and an identity was resolved for the request. | Key | Description | | - | - | | `user.id` | Unique identifier of the user | | `user.email` | User's email address | | `user.name` | User's display name | | `user.role` | Organization role (for example `owner`, `admin`, `member`) | ### Client Describes the MCP agent that made the request, taken from the agent's `clientInfo`. | Key | Description | | - | - | | `client.id` | Raw agent name as advertised by the agent | | `client.type` | Resolved agent type used to join traces against configuration drift data | | `client.version` | Agent version string | `client.type` is the normalized value (for example `claude_desktop`, `cursor`, `vscode`), while `client.id` preserves whatever the agent reported. Traces recorded before the normalization was introduced carry only `client.id`, so queries that need to cover historical data should fall back to it. ### Server Describes the upstream connector that handled the request. Which keys appear depends on the transport. `server.transport` reflects how the connector is configured, not the protocol that was negotiated at connection time. | Key | Description | | - | - | | `server.transport` | Transport type: `stdio`, `streamable-http`, or `sse` | | `server.pid` | OS process ID of the upstream connector. Local (`stdio`) connectors only | | `server.command` | Command used to launch the connector. Local (`stdio`) connectors only | | `server.binary_path` | Resolved path to the connector binary. Local (`stdio`) connectors only | | `server.url` | Endpoint the request was sent to. Remote (`streamable-http`, `sse`) connectors only | | `server.auth_method` | How Ultra authenticated to the upstream connector. See [Authentication context](/observability/audit-log#authentication-context) for the possible values | ## Querying Traces Traces are stored locally and can be queried through the dashboard. ### Dashboard ```bash theme={null} ultra dashboard ``` The dashboard at `http://localhost:8080` provides: * A list of all traces, sorted by timestamp * Filtering by operation type, upstream connector, status, and time range * Full request/response payload inspection for each trace * Duration and status breakdown * A **Copy link** button on each trace to share a deep link to it, and an **Export** control to download the filtered trace list as CSV or JSON (see [Exporting table data](/observability/dashboard#exporting-table-data) and [Sharing links](/observability/dashboard#sharing-links)) ### Aggregate Statistics Aggregate statistics are computed over your traces: * **Total requests** — Count of all operations * **Success/error counts** — How many succeeded vs failed * **Average duration** — Mean operation time * **Requests by type** — Breakdown by operation type * **Requests by connector** — Breakdown by upstream connector ## OpenTelemetry Compatibility Trace IDs and span IDs follow the [W3C Trace Context](https://www.w3.org/TR/trace-context/) specification, making Ultra traces compatible with any OpenTelemetry-compatible backend. See [OTLP Export](/observability/otlp-export) for configuration.