Helps users generate, edit, and validate mirrord.json configuration files for mirrord (MetalBear). Use when the user wants to connect their local process to a Kubernetes environment, configure features (env/fs/network), or needs feedback on an existing mirrord.json. Always ensures output JSON is valid and schema-conformant.
75
94%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Getting Started",
"description": "mirrord allows for a high degree of customization when it comes to which features you want to\nenable, and how they should function.\n\nAll of the configuration fields have a default value, so a minimal configuration would be no\nconfiguration at all.\n\nThe configuration supports [templating](#root-templating), so values can be derived at runtime\ninstead of hardcoded.\n\nTo use a configuration file in the CLI, use the `-f <CONFIG_PATH>` flag.\nOr if using VSCode Extension or JetBrains plugin, simply create a `.mirrord/mirrord.json` file\nor use the UI.\n\n## Templating {#root-templating}\n\nConfig files are rendered with the [Tera](https://keats.github.io/tera/) template engine before\nthey are parsed, so Tera's built-in functions and filters all work. On top of those, mirrord\nprovides these variables:\n\n- `key` - the [session key](#root-key), either the one you provided or the one mirrord generated\n for this session.\n- `git_branch` - the branch checked out in the working directory mirrord was started from. Set\n `MIRRORD_BRANCH_NAME` to override it; the JetBrains plugin does exactly that, with the branch\n of the project you have open.\n\nmirrord generates a session key for you, and you can reference it as `{{ key }}` in your HTTP\nfilter like so:\n\n```json\n{\n \"feature\": {\n \"network\": {\n \"incoming\": {\n \"mode\": \"steal\",\n \"http_filter\": {\n \"header_filter\": \"^baggage: .*mirrord-session={{ key }}.*$\"\n }\n }\n }\n }\n}\n```\n\nIt also supports setting your git branch as the key, so that each branch gets its own session:\n\n```json\n{\n \"key\": \"{{ git_branch }}\",\n \"feature\": {\n \"network\": {\n \"incoming\": {\n \"mode\": \"steal\",\n \"http_filter\": {\n \"header_filter\": \"^baggage: .*mirrord-session={{ key }}.*$\"\n }\n }\n }\n }\n}\n```\n\n### Templating the `key` field {#root-templating-key}\n\nThe [`key`](#root-key) field is read out of the config file *before* any templating happens, to\nbreak the cycle where the key is needed to render templates but is itself defined in the file\nbeing rendered. Two consequences:\n\n1. The file has to stay valid JSON/TOML/YAML as written. A double-quoted string inside a `key`\n template ends the surrounding JSON string early, so the `key` field is silently ignored and\n mirrord falls back to a generated key, leaving a session that looks healthy but filters on\n the wrong value. Tera accepts single-quoted string literals, so use those in `key`:\n `default(value='shared')` rather than `default(value=\"shared\")`. Every other field is\n rendered before parsing and accepts either quote style.\n2. Only variables that don't depend on the key are available there, which today means\n `git_branch`.\n\n### When a variable is undefined {#root-templating-undefined}\n\n`git_branch` is left out of the context entirely when the branch can't be determined - the\ndirectory isn't a git repository, `git` isn't installed, or `HEAD` is detached, which is the\nusual state in CI. Referencing it then fails the render with\n``Variable `git_branch` not found in context``, rather than quietly resolving to an empty\nstring and producing a filter that matches nothing.\n\nGive configs that also have to work in those environments a fallback:\n\n```json\n{\n \"key\": \"{{ git_branch | default(value='shared') }}\"\n}\n```\n\nIf you want us to provide any other value, please let us know.\n\n## Examples\n\nTo help you get started, here are examples of a basic configuration file, and a complete\nconfiguration file containing all fields.\n\n### Basic `config.json` {#root-basic}\n\n```json\n{\n \"target\": \"pod/bear-pod\",\n \"feature\": {\n \"env\": true,\n \"fs\": \"read\",\n \"network\": true\n }\n}\n```\n\n### Basic `config.json` with templating {#root-basic-templating}\n\n```json\n{\n \"target\": \"{{ get_env(name=\"TARGET\", default=\"pod/fallback\") }}\",\n \"feature\": {\n \"env\": true,\n \"fs\": \"read\",\n \"network\": true\n }\n}\n```\n\n### Complete `config.json` {#root-complete}\n\n Don't use this example as a starting point, it's just here to show you all the available\n options.\n```json\n{\n \"accept_invalid_certificates\": false,\n \"skip_processes\": \"ide-debugger\",\n \"target\": {\n \"path\": \"pod/bear-pod\",\n \"namespace\": \"default\"\n },\n \"connect_tcp\": null,\n \"agent\": {\n \"log_level\": \"info\",\n \"json_log\": false,\n \"labels\": { \"user\": \"meow\" },\n \"annotations\": { \"cats.io/inject\": \"enabled\" },\n \"namespace\": \"default\",\n \"image\": \"ghcr.io/metalbear-co/mirrord:latest\",\n \"image_pull_policy\": \"IfNotPresent\",\n \"image_pull_secrets\": [ { \"name\": \"secret\" } ],\n \"ttl\": 30,\n \"ephemeral\": false,\n \"communication_timeout\": 30,\n \"startup_timeout\": 360,\n \"flush_connections\": true,\n \"metrics\": \"0.0.0.0:9000\",\n },\n \"feature\": {\n \"env\": {\n \"include\": \"DATABASE_USER;PUBLIC_ENV\",\n \"exclude\": \"DATABASE_PASSWORD;SECRET_ENV\",\n \"override\": {\n \"DATABASE_CONNECTION\": \"db://localhost:7777/my-db\",\n \"LOCAL_BEAR\": \"panda\"\n },\n \"mapping\": {\n \".+_TIMEOUT\": \"1000\"\n }\n },\n \"fs\": {\n \"mode\": \"write\",\n \"read_write\": \".+\\\\.json\" ,\n \"read_only\": [ \".+\\\\.yaml\", \".+important-file\\\\.txt\" ],\n \"local\": [ \".+\\\\.js\", \".+\\\\.mjs\" ],\n \"prefetch\": [ \"/etc/ssl\" ]\n },\n \"network\": {\n \"incoming\": {\n \"mode\": \"steal\",\n \"http_filter\": {\n \"header_filter\": \"^baggage: .*mirrord-session={{ key }}.*$\"\n },\n \"port_mapping\": [[ 7777, 8888 ]],\n \"ignore_localhost\": false,\n \"ignore_ports\": [9999, 10000]\n },\n \"outgoing\": {\n \"tcp\": true,\n \"udp\": true,\n \"filter\": {\n \"local\": [\"tcp://1.1.1.0/24:1337\", \"1.1.5.0/24\", \"google.com\", \":53\"]\n },\n \"ignore_localhost\": false,\n \"unix_streams\": \"bear.+\"\n },\n \"dns\": {\n \"enabled\": true,\n \"filter\": {\n \"local\": [\"1.1.1.0/24:1337\", \"1.1.5.0/24\", \"google.com\"]\n }\n }\n },\n \"copy_target\": {\n \"scale_down\": false\n }\n },\n \"operator\": true,\n \"kubeconfig\": \"~/.kube/config\",\n \"sip_binaries\": \"bash\",\n \"telemetry\": true,\n \"kube_context\": \"my-cluster\"\n}\n```\n\n# Options {#root-options}",
"type": "object",
"properties": {
"accept_invalid_certificates": {
"title": "accept_invalid_certificates {#root-accept_invalid_certificates}",
"description": "Controls whether or not mirrord accepts invalid TLS certificates (e.g. self-signed\ncertificates).\n\nIf not provided, mirrord will use value from the kubeconfig.",
"type": [
"boolean",
"null"
]
},
"agent": {
"title": "agent {#root-agent}",
"anyOf": [
{
"$ref": "#/$defs/AgentFileConfig"
},
{
"type": "null"
}
]
},
"api": {
"title": "api {#root-api}",
"description": "Enables the local session monitor API server.\n\nWhen enabled, mirrord exposes a Unix socket at `~/.mirrord/sessions/<session-id>.sock`\nwith HTTP endpoints for observing session activity in real time.\n\nDefaults to `true`.",
"type": [
"boolean",
"null"
]
},
"baggage": {
"title": "baggage {#root-baggage}",
"description": "OpenTelemetry (OTel) / W3C baggage propagator. This is used in HTTP requests sent to the\noperator to manually set values in the trace span, which can help when processing traces.\nSee [OTel docs](https://opentelemetry.io/docs/specs/otel/context/env-carriers/#environment-variable-names)\n\nOnly relevant for use with the operator. For more details, read the [docs on monitoring](https://metalbear.com/mirrord/docs/managing-mirrord/monitoring).",
"type": [
"string",
"null"
]
},
"ci": {
"title": "ci {#root-ci}",
"anyOf": [
{
"$ref": "#/$defs/CiFileConfig"
},
{
"type": "null"
}
]
},
"container": {
"title": "container {#root-container}",
"anyOf": [
{
"$ref": "#/$defs/ContainerFileConfig"
},
{
"type": "null"
}
]
},
"experimental": {
"title": "experimental {#root-experimental}",
"anyOf": [
{
"$ref": "#/$defs/ExperimentalFileConfig"
},
{
"type": "null"
}
]
},
"external_proxy": {
"title": "external_proxy {#root-external_proxy}",
"anyOf": [
{
"$ref": "#/$defs/ExternalProxyFileConfig"
},
{
"type": "null"
}
]
},
"feature": {
"title": "feature {#root-feature}",
"anyOf": [
{
"$ref": "#/$defs/FeatureFileConfig"
},
{
"type": "null"
}
]
},
"internal_proxy": {
"title": "internal_proxy {#root-internal_proxy}",
"anyOf": [
{
"$ref": "#/$defs/InternalProxyFileConfig"
},
{
"type": "null"
}
]
},
"key": {
"title": "key {#root-key}",
"description": "An identifier for a mirrord session.\n\nThe key can be referenced in your configuration using the `{{ key }}` template variable.\n\nThe recommended use is to propagate it in W3C `baggage` or `tracestate`, then filter on\n`mirrord-session={{ key }}` in `feature.network.incoming.http_filter`.\n\n```json\n{\n \"key\": \"my-session-key\",\n \"feature\": {\n \"network\": {\n \"incoming\": {\n \"http_filter\": {\n \"header_filter\": \"^baggage: .*mirrord-session={{ key }}.*$\"\n }\n }\n }\n }\n}\n```\n\nThe key itself is [templated](#root-templating-key), so instead of a literal it can be\nderived from another variable. Using `git_branch` gives each branch its own session\nwithout passing a key per run:\n\n```json\n{\n \"key\": \"{{ git_branch }}\"\n}\n```\n\nPriority (highest to lowest):\n1. CLI argument: `mirrord exec --key my-key`\n2. Environment variable: `MIRRORD_KEY`\n3. Config file: `{ \"key\": \"my-key\" }`\n4. Fallback: A unique key is randomly generated if no other option is provided",
"anyOf": [
{
"$ref": "#/$defs/EnvKeyFileConfig"
},
{
"type": "null"
}
]
},
"kube_context": {
"title": "kube_context {#root-kube_context}",
"description": "Kube context to use from the kubeconfig file.\nWill use current context if not specified.\n\n```json\n{\n \"kube_context\": \"mycluster\"\n}\n```",
"type": [
"string",
"null"
]
},
"kubeconfig": {
"title": "kubeconfig {#root-kubeconfig}",
"description": "Path to a kubeconfig file, if not specified, will use `KUBECONFIG`, or `~/.kube/config`, or\nthe in-cluster config.\n\n```json\n{\n \"kubeconfig\": \"~/bear/kube-config\"\n}\n```",
"type": [
"string",
"null"
]
},
"multi_cluster": {
"title": "multi_cluster {#root-multi_cluster}",
"description": "Controls multi-cluster session behavior when connecting to a multi-cluster Primary\noperator. When set to `false`, forces a single-cluster session on the Primary cluster\ninstead of creating a multi-cluster session that spans all workload clusters.\nWhen `true` or unset, multi-cluster sessions are used when the operator supports them.",
"type": [
"boolean",
"null"
]
},
"operator": {
"title": "operator {#root-operator}",
"description": "Whether mirrord should use the operator.\nIf not set, mirrord will first attempt to use the operator, but continue without it in case\nof failure.",
"type": [
"boolean",
"null"
]
},
"profile": {
"title": "profile {#root-profile}",
"description": "Name of the mirrord profile to use.\n\nTo select a cluster-wide profile\n\n```json\n{\n \"profile\": \"my-profile-name\"\n}\n```\n\nTo select a namespaced profile\n\n```json\n{\n \"profile\": \"my-namespace/my-profile-name\"\n}\n```",
"type": [
"string",
"null"
]
},
"sip_binaries": {
"title": "sip_binaries {#root-sip_binaries}",
"description": "Binaries to patch (macOS SIP).\n\nUse this when mirrord isn't loaded to protected binaries that weren't automatically\npatched.\n\nRuns `endswith` on the binary path (so `bash` would apply to any binary ending with `bash`\nwhile `/usr/bin/bash` would apply only for that binary).\n\n```json\n{\n \"sip_binaries\": [\"bash\", \"python\"]\n}\n```",
"anyOf": [
{
"$ref": "#/$defs/VecOrSingle"
},
{
"type": "null"
}
]
},
"skip_build_tools": {
"title": "skip_build_tools {#root-skip_build_tools}",
"description": "Allows mirrord to skip build tools. Useful when running command lines that build and run\nthe application in a single command.\n\nDefaults to `true`.\n\nBuild-Tools: `[\"as\", \"cc\", \"ld\", \"go\", \"air\", \"asm\", \"cc1\", \"cgo\", \"dlv\", \"gcc\", \"git\",\n\"link\", \"math\", \"cargo\", \"hpack\", \"rustc\", \"compile\", \"collect2\", \"cargo-watch\",\n\"debugserver\"]`",
"type": [
"boolean",
"null"
]
},
"skip_extra_build_tools": {
"title": "skip_extra_build_tools {#root-skip_build_tools}",
"description": "Allows mirrord to skip the specified build tools. Useful when running command lines that\nbuild and run the application in a single command.\n\nMust also enable [`skip_build_tools`](#root-skip_build_tools) for this to take an effect.\n\nIt's similar to [`skip_processes`](#root-skip_processes), except that here it also skips\nSIP patching.\n\nAccepts a single value, or an array of values.\n\n```json\n{\n \"skip_extra_build_tools\": [\"bash\", \"node\"]\n}\n```",
"anyOf": [
{
"$ref": "#/$defs/VecOrSingle"
},
{
"type": "null"
}
]
},
"skip_processes": {
"title": "skip_processes {#root-skip_processes}",
"description": "Allows mirrord to skip unwanted processes.\n\nUseful when process A spawns process B, and the user wants mirrord to operate only on\nprocess B.\nAccepts a single value, or an array of values.\n\n```json\n{\n \"skip_processes\": [\"bash\", \"node\"]\n}\n```",
"anyOf": [
{
"$ref": "#/$defs/VecOrSingle"
},
{
"type": "null"
}
]
},
"skip_sip": {
"title": "skip_sip {#root-skip_sip}",
"description": "Allows mirrord to skip patching (macOS SIP) unwanted processes.\n\nWhen patching is skipped, mirrord will no longer be able to load into\nthe process and its child processes.\n\nDefaults to `{ \"skip_sip\": \"git\" }`\n\nWhen specified, the given value will replace the default list rather than\nbeing added to.",
"anyOf": [
{
"$ref": "#/$defs/VecOrSingle"
},
{
"type": "null"
}
]
},
"startup_retry": {
"title": "startup_retry {#root-startup_retry}",
"anyOf": [
{
"$ref": "#/$defs/StartupRetryFileConfig"
},
{
"type": "null"
}
]
},
"target": {
"title": "target {#root-target}",
"anyOf": [
{
"$ref": "#/$defs/TargetFileConfig"
},
{
"type": "null"
}
]
},
"telemetry": {
"title": "telemetry {#root-telemetry}",
"description": "Controls whether or not mirrord sends telemetry data to MetalBear cloud.\nTelemetry sent doesn't contain personal identifiers or any data that\nshould be considered sensitive. It is used to improve the product.\n[For more information](https://github.com/metalbear-co/mirrord/blob/main/TELEMETRY.md)",
"type": [
"boolean",
"null"
]
},
"traceparent": {
"title": "traceparent {#root-traceparent}",
"description": "OpenTelemetry (OTel) / W3C trace context. This is used in HTTP requests sent to the\noperator to manually set the parent trace of the entry point, which can help when\nprocessing traces.\nSee [OTel docs](https://opentelemetry.io/docs/specs/otel/context/env-carriers/#environment-variable-names)\n\nOnly relevant for use with the operator. For more details, read the [docs on monitoring](https://metalbear.com/mirrord/docs/managing-mirrord/monitoring).",
"type": [
"string",
"null"
]
},
"use_proxy": {
"title": "use_proxy {#root-use_proxy}",
"description": "When disabled, mirrord will remove `HTTP[S]_PROXY` env variables before\ndoing any network requests. This is useful when the system sets a proxy\nbut you don't want mirrord to use it.\nThis also applies to the mirrord process (as it just removes the env).\nIf the remote pod sets this env, the mirrord process will still use it.",
"type": [
"boolean",
"null"
]
}
},
"additionalProperties": false,
"$defs": {
"AdvancedFsUserConfig": {
"description": "Allows the user to specify the default behavior for file operations:\n\n1. `\"read\"` or `true` - Read from the remote file system (default)\n2. `\"write\"` - Read/Write from the remote file system.\n3. `\"local\"` or `false` - Read from the local file system.\n4. `\"localwithoverrides\"` - perform fs operation locally, unless the path matches a pre-defined\n or user-specified exception.\n\n> Note: by default, some paths are read locally or remotely, regardless of the selected FS mode.\n> This is described in further detail below.\n\nBesides the default behavior, the user can specify behavior for specific regex patterns.\nCase insensitive.\n\n1. `\"read_write\"` - List of patterns that should be read/write remotely.\n2. `\"read_only\"` - List of patterns that should be read only remotely.\n3. `\"local\"` - List of patterns that should be read locally.\n4. `\"not_found\"` - List of patterns that should never be read nor written. These files should be\n treated as non-existent.\n4. `\"mapping\"` - Map of patterns and their corresponding replacers. The replacement happens before any specific behavior as defined above or mode (uses [`Regex::replace`](https://docs.rs/regex/latest/regex/struct.Regex.html#method.replace))\n\nThe logic for choosing the behavior is as follows:\n\n\n1. Check against \"mapping\" if path needs to be replaced, if matched then continue to next step\n with new path after replacements otherwise continue as usual.\n2. Check if one of the patterns match the file path, do the corresponding action. There's no\n specified order if two lists match the same path, we will use the first one (and we do not\n guarantee what is first).\n\n **Warning**: Specifying the same path in two lists is unsupported and can lead to undefined\n behaviour.\n\n3. There are pre-defined exceptions to the set FS mode.\n 1. Paths that match the pre-defined patterns [for Linux/MacOS](https://github.com/metalbear-co/mirrord/tree/latest/mirrord/layer-lib/src/file/unix/read_local_by_default.rs)\n or [for Windows](https://github.com/metalbear-co/mirrord/tree/latest/mirrord/layer-lib/src/file/windows/read_local_by_default.rs)\n are read locally by default.\n 2. Paths that match the pre-defined patterns [for Linux/MacOS](https://github.com/metalbear-co/mirrord/tree/latest/mirrord/layer-lib/src/file/unix/read_remote_by_default.rs)\n or [for Windows](https://github.com/metalbear-co/mirrord/tree/latest/mirrord/layer-lib/src/file/windows/read_remote_by_default.rs)\n are read remotely by default when the mode is `localwithoverrides`.\n 3. Paths that match the pre-defined patterns [for Linux/MacOS](https://github.com/metalbear-co/mirrord/tree/latest/mirrord/layer-lib/src/file/unix/not_found_by_default.rs)\n or [for Windows](https://github.com/metalbear-co/mirrord/tree/latest/mirrord/layer-lib/src/file/windows/not_found_by_default.rs)\n under the running user's home directory will not be found by the application when the mode\n is not `local`.\n\n In order to override that default setting for a path, or a pattern, include it the\n appropriate pattern set from above. E.g. in order to read files under `/etc/` remotely even\n though it is covered by the set of pre-defined patterns that are read locally by default,\n add `\"^/etc/.\"` to the `read_only` set.\n\n4. If none of the above match, use the default behavior (mode).\n\nFor more information, check the file operations\n[technical reference](https://metalbear.com/mirrord/docs/reference/fileops/).\n\n```json\n{\n \"feature\": {\n \"fs\": {\n \"mode\": \"write\",\n \"read_write\": \".+\\\\.json\" ,\n \"read_only\": [ \".+\\\\.yaml\", \".+important-file\\\\.txt\" ],\n \"local\": [ \".+\\\\.js\", \".+\\\\.mjs\" ],\n \"not_found\": [ \"\\\\.config/gcloud\" ],\n \"prefetch\": [ \"/etc/ssl\" ]\n }\n }\n}\n```",
"type": "object",
"properties": {
"local": {
"title": "feature.fs.local {#feature-fs-local}",
"description": "Specify file path patterns that if matched will be opened locally.\n\n##### Windows\n\nPatterns are matched against a unix-style form of the requested path, not the raw\nWindows path. Before matching, the drive letter is stripped and backslashes are\nconverted to forward slashes, so `D:\\Workspaces\\myapp\\app.json` is matched as\n`/Workspaces/myapp/app.json`.\n\nPatterns must therefore be written with forward slashes. Backslashes in the regex\n(e.g. `\"\\\\\\\\Workspaces\\\\\\\\\"`) will never match anything, because the input string the\nregex sees contains no backslashes at all — they were stripped during translation.",
"anyOf": [
{
"$ref": "#/$defs/VecOrSingle"
},
{
"type": "null"
}
]
},
"mapping": {
"title": "feature.fs.mapping {#feature-fs-mapping}",
"description": "Specify map of patterns that if matched will replace the path according to specification.\n\n*Capture groups are allowed.* Matching is case-insensitive and uses\n[`Regex::replace`](https://docs.rs/regex/latest/regex/struct.Regex.html#method.replace),\nso the replacement value supports `$1` / `${name}` substitutions.\n\n##### Unix (Linux, macOS)\n\nPatterns are matched against the path exactly as the application requested it.\n\nExample:\n```json\n{\n \"^/home/(?<user>\\\\S+)/dev/tomcat\": \"/etc/tomcat\",\n \"^/home/(?<user>\\\\S+)/dev/config/(?<app>\\\\S+)\": \"/mnt/configs/${user}-$app\"\n}\n```\n\nWill produce the following replacements for any io operation:\n\n`/home/johndoe/dev/tomcat/context.xml` => `/etc/tomcat/context.xml`\n`/home/johndoe/dev/config/api/app.conf` => `/mnt/configs/johndoe-api/app.conf`\n\n##### Windows\n\nPatterns are matched against a unix-style form of the requested path, not the raw\nWindows path. Before matching, the drive letter is stripped and backslashes are\nconverted to forward slashes, so:\n\n`D:\\Workspaces\\myapp\\config\\app.json` is matched as `/Workspaces/myapp/config/app.json`\n\nPatterns must therefore be written with forward slashes. Backslashes in the regex\n(e.g. `\"\\\\\\\\Workspaces\\\\\\\\\"`) will never match anything, because the input string the\nregex sees contains no backslashes at all — they were stripped during translation.\n\nThe replacement value is sent to the agent as-is and is what the remote (Linux) pod\nwill see on disk, so it should also use forward slashes.\n\nExample:\n```json\n{\n \"^/Workspaces/(?<app>[^/]+)/config/(?<file>.+)\": \"/etc/${app}/$file\"\n}\n```\n\nWill produce the following replacement:\n\n`D:\\Workspaces\\myapp\\config\\app.json` => `/etc/myapp/app.json`\n\n##### Caveats\n\n- Relative paths: this feature (currently) does not apply mappings to relative paths, e.g.\n `../dev`.",
"type": [
"object",
"null"
],
"additionalProperties": {
"type": "string"
}
},
"mode": {
"title": "feature.fs.mode {#feature-fs-mode}",
"anyOf": [
{
"$ref": "#/$defs/FsModeConfig"
},
{
"type": "null"
}
]
},
"not_found": {
"title": "feature.fs.not_found {#feature-fs-not_found}",
"description": "Specify file path patterns that if matched will be treated as non-existent.\n\n##### Windows\n\nPatterns are matched against a unix-style form of the requested path, not the raw\nWindows path. Before matching, the drive letter is stripped and backslashes are\nconverted to forward slashes, so `D:\\Workspaces\\myapp\\app.json` is matched as\n`/Workspaces/myapp/app.json`.\n\nPatterns must therefore be written with forward slashes. Backslashes in the regex\n(e.g. `\"\\\\\\\\Workspaces\\\\\\\\\"`) will never match anything, because the input string the\nregex sees contains no backslashes at all — they were stripped during translation.",
"anyOf": [
{
"$ref": "#/$defs/VecOrSingle"
},
{
"type": "null"
}
]
},
"prefetch": {
"title": "feature.fs.prefetch {#feature-fs-prefetch}",
"description": "Remote paths to download from the target before the local process starts.\n\nEach path is copied from the remote filesystem into a temporary local directory, along\nwith its permissions. Operations on these paths are then served from that local copy,\nwithout involving the agent at all. Directories are copied recursively.\n\nWrites go to the copy as well, and are never sent to the target. The application reads\nback whatever it wrote for the rest of the run, and the copy is abandoned when the run\nends, leaving the target untouched. A write reaches the copy only when the\n[`mode`](#feature-fs-mode) would otherwise have sent it to the target, that is under\n`write`; under the other modes writes stay on the local filesystem under their original\npath, as they do for every other file.\n\nThe copy keeps the permissions it had in the target, so a file that is read-only there is\nread-only here, and writing to it fails locally much as it would remotely.\n\n**Do not prefetch secrets.** The copy is written to a temporary directory on the machine\nrunning mirrord, guarded by nothing more than that machine's own permissions, rather than\nby the cluster. It is deleted when the session ends, but a session that is killed outright\nleaves it behind, since nothing gets to run.\n\nCreating, deleting and renaming still act on the target: a file created under a prefetched\ndirectory is not part of the copy, and deleting or renaming a prefetched path takes effect\nremotely rather than in the copy.\n\nThis trades startup time for read throughput, and is meant for applications that\nrepeatedly read a small and stable set of remote files, e.g. an HTTP server that reads\n`/etc/ssl` on every request.\n\nBecause the copy is taken once, before the application starts, changes made to these\npaths in the pod afterwards are invisible to the application.\n\nPaths must start with `/`, as they are resolved in the remote pod, where the local\nprocess's working directory is meaningless.\n\nNot supported on Windows, where the option is ignored.\n\n```json\n{\n \"feature\": {\n \"fs\": {\n \"mode\": \"read\",\n \"prefetch\": [ \"/etc/ssl\", \"/app/config.yaml\" ]\n }\n }\n}\n```",
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
},
"prefetch_timeout": {
"title": "feature.fs.prefetch_timeout {#feature-fs-prefetch_timeout}",
"description": "How long to wait, in seconds, for each request to the agent made while copying\n[`prefetch`](#feature-fs-prefetch) paths. By default, the value is 30 seconds.\n\nThe limit is per request rather than for the whole copy, which makes many requests: one\nper directory listing, and one per chunk of every file. Raise it when copying from a slow\nor distant cluster, where a single request can outlast the default.\n\nA path that runs out of time is skipped with a warning, and is read from the remote as\nusual.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
},
"read_only": {
"title": "feature.fs.read_only {#feature-fs-read_only}",
"description": "Specify file path patterns that if matched will be read from the remote.\nif file matching the pattern is opened for writing or read/write it will be opened locally.\n\n##### Windows\n\nPatterns are matched against a unix-style form of the requested path, not the raw\nWindows path. Before matching, the drive letter is stripped and backslashes are\nconverted to forward slashes, so `D:\\Workspaces\\myapp\\app.json` is matched as\n`/Workspaces/myapp/app.json`.\n\nPatterns must therefore be written with forward slashes. Backslashes in the regex\n(e.g. `\"\\\\\\\\Workspaces\\\\\\\\\"`) will never match anything, because the input string the\nregex sees contains no backslashes at all — they were stripped during translation.",
"anyOf": [
{
"$ref": "#/$defs/VecOrSingle"
},
{
"type": "null"
}
]
},
"read_write": {
"title": "feature.fs.read_write {#feature-fs-read_write}",
"description": "Specify file path patterns that if matched will be read and written to the remote.\n\n##### Windows\n\nPatterns are matched against a unix-style form of the requested path, not the raw\nWindows path. Before matching, the drive letter is stripped and backslashes are\nconverted to forward slashes, so `D:\\Workspaces\\myapp\\app.json` is matched as\n`/Workspaces/myapp/app.json`.\n\nPatterns must therefore be written with forward slashes. Backslashes in the regex\n(e.g. `\"\\\\\\\\Workspaces\\\\\\\\\"`) will never match anything, because the input string the\nregex sees contains no backslashes at all — they were stripped during translation.",
"anyOf": [
{
"$ref": "#/$defs/VecOrSingle"
},
{
"type": "null"
}
]
},
"readonly_file_buffer": {
"title": "feature.fs.readonly_file_buffer {#feature-fs-readonly_file_buffer}",
"description": "Sets buffer size for read-only remote files in bytes. By default, the value is\n128000 bytes, or 128 kB.\n\nSetting the value to 0 disables file buffering.\nOtherwise, read-only remote files will be read in chunks and buffered locally.\nThis improves performance when the user application reads data in small portions.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
}
},
"additionalProperties": false
},
"AgentFileConfig": {
"description": "Configuration for the mirrord-agent pod that is spawned in the Kubernetes cluster.\n\n**Note:** this configuration is ignored when using the mirrord Operator.\nAgent configuration is done by the cluster admin.\n\nWe provide sane defaults for this option, so you don't have to set up anything here.\n\n```json\n{\n \"agent\": {\n \"log_level\": \"info\",\n \"json_log\": false,\n \"namespace\": \"default\",\n \"image\": \"ghcr.io/metalbear-co/mirrord:latest\",\n \"image_pull_policy\": \"IfNotPresent\",\n \"image_pull_secrets\": [ { \"name\": \"secret\" } ],\n \"ttl\": 30,\n \"ephemeral\": false,\n \"communication_timeout\": 30,\n \"startup_timeout\": 360,\n \"flush_connections\": false,\n \"exclude_from_mesh\": false\n \"inject_headers\": false,\n \"override_cache_control\": true,\n \"max_body_buffer_size\": 65535,\n \"max_body_buffer_timeout\": 1000,\n \"http_detection_timeout\": 2\n }\n}\n```",
"type": "object",
"properties": {
"annotations": {
"title": "agent.annotations {#agent-annotations}",
"description": "Allows setting up custom annotations for the agent Job and Pod.\n\n```json\n{\n \"agent\": {\n \"annotations\": {\n \"cats.io/inject\": \"enabled\"\n \"prometheus.io/scrape\": \"true\",\n \"prometheus.io/port\": \"9000\"\n }\n }\n}\n```",
"type": [
"object",
"null"
],
"additionalProperties": {
"type": "string"
}
},
"check_out_of_pods": {
"title": "agent.check_out_of_pods {#agent-check_out_of_pods}",
"description": "Determine if to check whether there is room for agent job in target node. (Not applicable\nwhen using ephemeral containers feature)\n\nCan be disabled if the check takes too long and you are sure there is enough resources on\neach node",
"type": [
"boolean",
"null"
]
},
"clean_iptables_on_start": {
"title": "agent.clean_iptables_on_start {#agent-clean_iptables_on_start}",
"description": "Clean leftover iptables rules and start the new agent instead of erroring out when there\nare existing mirrord rules in the target's iptables.",
"type": [
"boolean",
"null"
]
},
"communication_timeout": {
"title": "agent.communication_timeout {#agent-communication_timeout}",
"description": "Controls how long the agent lives when there are no connections.\n\nEach connection has its own heartbeat mechanism, so even if the local application has no\nmessages, the agent stays alive until there are no more heartbeat messages.",
"type": [
"integer",
"null"
],
"format": "uint16",
"maximum": 65535,
"minimum": 0
},
"disable_mesh_sidecar_injection": {
"title": "agent.disable_mesh_sidecar_injection {#agent-disable_mesh_sidecar_injection}",
"description": "Add relevant labels and annotations to agent pods/jobs to\nprevent service mesh sidecar injections. Defaults to true.\n\nOnly affects istio, linkerd, kuma.",
"type": [
"boolean",
"null"
]
},
"disabled_capabilities": {
"title": "agent.disabled_capabilities {#agent-disabled_capabilities}",
"description": "If nothing is disabled here, agent uses:\n1. `NET_ADMIN`,\n2. `SYS_PTRACE`,\n3. `SYS_ADMIN`.\n\nHas no effect when using the targetless mode,\nas targetless agent containers have no capabilities.",
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
},
"dns": {
"title": "agent.dns {#agent-dns}",
"anyOf": [
{
"$ref": "#/$defs/FileAgentDnsConfig"
},
{
"type": "null"
}
]
},
"ephemeral": {
"title": "agent.ephemeral {#agent-ephemeral}",
"description": "Runs the agent as an\n[ephemeral container](https://kubernetes.io/docs/concepts/workloads/pods/ephemeral-containers/).\n\nNot compatible with targetless runs.\n\nDefaults to `false`.",
"type": [
"boolean",
"null"
]
},
"exclude_from_mesh": {
"title": "agent.exclude_from_mesh {#agent-exclude_from_mesh}",
"description": "When running the agent as an ephemeral container, use this option to exclude\nthe agent's port from the service mesh sidecar proxy.",
"type": [
"boolean",
"null"
]
},
"external_ip_fix": {
"title": "agent.external_ip_fix {#agent-external_ip_fix}",
"description": "Fixes passthrough requests failing when the target application listens on the pod's\nexternal IP instead of `127.0.0.1` (e.g. because it resolves its own pod name and binds\nto the pod IP).\n\nWhen enabled, the agent passes redirected connections through to their original destination\nIP rather than to loopback. To avoid an iptables redirection loop, those connections are\nmarked and excluded from the redirect rules; this requires `SO_MARK` support.\n\nEnabled by default, set to `false` to pass redirected connections through to loopback.",
"type": [
"boolean",
"null"
]
},
"flush_connections": {
"title": "agent.flush_connections {#agent-flush_connections}",
"description": "Flushes existing connections when starting to steal, might fix issues where connections\naren't stolen (due to being already established)\n\nDefaults to `true`.",
"type": [
"boolean",
"null"
]
},
"http_detection_timeout": {
"title": "agent.http_detection_timeout {#agent-http_detection_timeout}",
"description": "How long, in seconds, to wait for data on a redirected connection during HTTP protocol\ndetection before giving up and treating it as raw TCP. The timer resets on every read,\nso total detection time may exceed this value for connections that send data in many\nsmall chunks.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
},
"image": {
"title": "agent.image {#agent-image}",
"description": "Name of the agent's docker image.\n\nUseful when a custom build of mirrord-agent is required, or when using an internal\nregistry.\n\nDefaults to the latest stable image `\"ghcr.io/metalbear-co/mirrord:latest\"`.\n\n```json\n{\n \"agent\": {\n \"image\": \"internal.repo/images/mirrord:latest\"\n }\n}\n```\n\nComplete setup:\n\n```json\n{\n \"agent\": {\n \"image\": {\n \"registry\": \"internal.repo/images/mirrord\",\n \"tag\": \"latest\"\n }\n }\n}\n```\n\nCan also be controlled via `MIRRORD_AGENT_IMAGE`, `MIRRORD_AGENT_IMAGE_REGISTRY`, and\n`MIRRORD_AGENT_IMAGE_TAG`. `MIRRORD_AGENT_IMAGE` takes precedence, followed by config\nvalues for registry/tag, then environment variables for registry/tag.",
"anyOf": [
{
"$ref": "#/$defs/AgentImageFileConfig"
},
{
"type": "null"
}
]
},
"image_pull_policy": {
"title": "agent.image_pull_policy {#agent-image_pull_policy}",
"description": "Controls when a new agent image is downloaded.\n\nSupports `\"IfNotPresent\"`, `\"Always\"`, `\"Never\"`, or any valid kubernetes\n[image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy)\n\nDefaults to `\"IfNotPresent\"`",
"type": [
"string",
"null"
]
},
"image_pull_secrets": {
"title": "agent.image_pull_secrets {#agent-image_pull_secrets}",
"description": "List of secrets the agent pod has access to.\n\nTakes an array of entries with the format `{ name: <secret-name> }`.\n\nRead more [here](https://kubernetes.io/docs/concepts/containers/images/#referring-to-an-imagepullsecrets-on-a-pod).\n\n```json\n{\n \"agent\": {\n \"image_pull_secrets\": [\n { \"name\": \"secret-key-1\" },\n { \"name\": \"secret-key-2\" }\n ]\n }\n}\n```",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/$defs/AgentPullSecret"
}
},
"inject_headers": {
"title": "agent.inject_headers {#agent-inject_headers}",
"description": "Sets whether `Mirrord-Agent` headers are injected into HTTP\nresponses that went through the agent.\n\nPossible values for the header:\n\n- `passed-through`: set when the request was not sent to the local app (perhaps because it\n didn't match the filters)\n\n- `forwarded-to-client`: set when the request was sent to the local app",
"type": [
"boolean",
"null"
]
},
"jaq_time_limit": {
"title": "agent.jaq_time_limit {#agent-jaq_time_limit}",
"description": "Time limit for running jaq queries, in milliseconds. Defaults to 500ms.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
},
"json_log": {
"title": "agent.json_log {#agent-json_log}",
"description": "Controls whether the agent produces logs in a human-friendly format, or json.\n\n```json\n{\n \"agent\": {\n \"json_log\": true\n }\n}\n```",
"type": [
"boolean",
"null"
]
},
"labels": {
"title": "agent.labels {#agent-labels}",
"description": "Allows setting up custom labels for the agent Job and Pod.\n\n```json\n{\n \"agent\": {\n \"labels\": { \"user\": \"meow\", \"state\": \"asleep\" }\n }\n}\n```",
"type": [
"object",
"null"
],
"additionalProperties": {
"type": "string"
}
},
"log_level": {
"title": "agent.log_level {#agent-log_level}",
"description": "Log level for the agent.\n\n\nSupports `\"trace\"`, `\"debug\"`, `\"info\"`, `\"warn\"`, `\"error\"`, or any string that would work\nwith `RUST_LOG`.\n\n```json\n{\n \"agent\": {\n \"log_level\": \"mirrord=debug,warn\"\n }\n}\n```",
"type": [
"string",
"null"
]
},
"max_body_buffer_size": {
"title": "agent.max_body_buffer_size {#agent-max_body_buffer_size}",
"description": "Maximum size, in bytes, of HTTP request body buffers. Used for\ntemporarily storing bodies of incoming HTTP requests to run\nbody filters. HTTP body filters will not match any requests\nwith bodies larger than this.",
"type": [
"integer",
"null"
],
"format": "uint32",
"minimum": 0
},
"max_body_buffer_timeout": {
"title": "agent.max_body_buffer_timeout {#agent-max_body_buffer_timeout}",
"description": "Maximum timeout, in milliseconds, for receiving HTTP request\nbodies. HTTP body filters will not match any requests whose\nbodies do not arrive within this timeout.",
"type": [
"integer",
"null"
],
"format": "uint32",
"minimum": 0
},
"metrics": {
"title": "agent.metrics {#agent-metrics}",
"description": "Enables prometheus metrics for the agent pod.\n\nYou might need to add annotations to the agent pod depending on how prometheus is\nconfigured to scrape for metrics.\n\n```json\n{\n \"agent\": {\n \"metrics\": \"0.0.0.0:9000\"\n }\n}\n```",
"type": [
"string",
"null"
]
},
"namespace": {
"title": "agent.namespace {#agent-namespace}",
"description": "Namespace where the agent shall live.\n\n**Note:** ignored in targetless runs or when the agent is run as an ephemeral container.\n\nDefaults to the current kubernetes namespace.",
"type": [
"string",
"null"
]
},
"nftables": {
"title": "agent.nftables {#agent-nftables}",
"description": "Determines which iptables backend will be used for traffic redirection.\n\nIf set to `true`, the agent will use iptables-nft.\nIf set to `false`, the agent will use iptables-legacy.\nIf not set, the agent will try to detect the correct backend at runtime.",
"type": [
"boolean",
"null"
]
},
"node_selector": {
"title": "agent.node_selector {#agent-node_selector}",
"description": "Allows setting up custom node selector for the agent Pod. Applies only to targetless runs,\nas targeted agent always runs on the same node as its target container.\n\n```json\n{\n \"agent\": {\n \"node_selector\": { \"kubernetes.io/hostname\": \"node1\" }\n }\n}\n```",
"type": [
"object",
"null"
],
"additionalProperties": {
"type": "string"
}
},
"override_cache_control": {
"title": "agent.override_cache_control {#agent-override_cache_control}",
"description": "Sets whether the `Cache-Control` header in HTTP responses that went through the agent is\nreplaced with `no-cache, no-store, must-revalidate`.\n\nResponses served while the target is redirected are not representative of the target's\nnormal output, so caching them (in the browser, or in a CDN in front of the cluster) both\nserves stale data to other users and hides subsequent requests from the agent.\n\nSet this to `false` to leave the original `Cache-Control` header untouched.",
"type": [
"boolean",
"null"
]
},
"priority_class": {
"title": "agent.priority_class {#agent-priority_class}",
"description": "Specifies the priority class to assign to the agent pod.\n\n```json\n{\n \"agent\": {\n \"priority_class\": \"my-priority-class-name\"\n }\n}\n```\n\nIn some cases, the agent pod may fail to schedule due to node resource constraints.\nSetting a priority class allows you to explicitly assign an existing priority class\nfrom your cluster to the agent pod, increasing its priority relative to other workloads.",
"type": [
"string",
"null"
]
},
"privileged": {
"title": "agent.privileged {#agent-privileged}",
"description": "Run the mirror agent as privileged container.\nDefaults to `false`.\n\nMight be needed in strict environments such as Bottlerocket.\n\nHas no effect when using the targetless mode,\nas targetless agent containers are never privileged.",
"type": [
"boolean",
"null"
]
},
"resources": {
"title": "agent.resources {#agent-resources}",
"description": "Set pod resource requirements. (not with ephemeral agents)\nDefault is\n```json\n{\n \"agent\": {\n \"resources\": {\n \"requests\":\n {\n \"cpu\": \"1m\",\n \"memory\": \"1Mi\"\n },\n \"limits\":\n {\n \"cpu\": \"1\",\n \"memory\": \"100Mi\"\n }\n }\n }\n}\n```",
"anyOf": [
{
"$ref": "#/$defs/io.k8s.api.core.v1.ResourceRequirements"
},
{
"type": "null"
}
]
},
"security_context": {
"title": "agent.security_context {#agent-security_context}",
"description": "Agent pod security context (not with ephemeral agents).\nSupport seccomp profile and app armor profile.",
"anyOf": [
{
"$ref": "#/$defs/SecurityContext"
},
{
"type": "null"
}
]
},
"service_account": {
"title": "agent.service_account {#agent-service_account}",
"description": "Allows setting up custom Service Account for the agent Job and Pod.\n\n```json\n{\n \"agent\": {\n \"service_account\": \"my-service-account\"\n }\n}\n```",
"type": [
"string",
"null"
]
},
"startup_timeout": {
"title": "agent.startup_timeout {#agent-startup_timeout}",
"description": "Controls how long to wait for the agent to finish initialization.\n\nIf initialization takes longer than this value, mirrord exits.\n\nDefaults to `60`.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
},
"tolerations": {
"title": "agent.tolerations {#agent-tolerations}",
"description": "Set pod tolerations. (not with ephemeral agents).\n\nDefaults to `operator: Exists`.\n\n```json\n{\n \"agent\": {\n \"tolerations\": [\n {\n \"key\": \"meow\", \"operator\": \"Exists\", \"effect\": \"NoSchedule\"\n }\n ]\n }\n}\n```\n\nSet to an empty array to have no tolerations at all",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/$defs/io.k8s.api.core.v1.Toleration"
}
},
"ttl": {
"title": "agent.ttl {#agent-ttl}",
"description": "Controls how long the agent pod persists for after the agent exits (in seconds).\n\nCan be useful for collecting logs.\n\nDefaults to `1`.",
"type": [
"integer",
"null"
],
"format": "uint16",
"maximum": 65535,
"minimum": 0
}
},
"additionalProperties": false
},
"AgentImageFileConfig": {
"description": "<!--${internal}-->\nAllows us to support the dual configuration for the agent image.\n\nWhatever values missing are replaced with our defaults.",
"anyOf": [
{
"description": "The shortened version of: `image: \"repo/mirrord:latest\"`.",
"type": [
"string",
"null"
]
},
{
"description": "Expanded version: `image: { registry: \"repo/mirrord\", tag: \"latest\" }`.",
"type": "object",
"properties": {
"registry": {
"type": [
"string",
"null"
]
},
"tag": {
"type": [
"string",
"null"
]
}
},
"additionalProperties": false
}
]
},
"AgentPullSecret": {
"description": "<!--${internal}-->\nSpecifies a secret reference for the agent pod.",
"type": "object",
"properties": {
"name": {
"description": "Name of the secret.",
"type": "string"
}
},
"additionalProperties": false,
"required": [
"name"
]
},
"AppleVariablesConfig": {
"type": "object",
"additionalProperties": false
},
"BodyFilter": {
"description": "Currently only JSON body filtering is supported.",
"oneOf": [
{
"title": "feature.network.incoming.inner_filter.body_filter.json {#feature-network-incoming-inner-body-filter-json}",
"description": "Tries to parse the body as a JSON object and find (a) matching subobjects(s).\n\n`query` should be a valid JSONPath (RFC 9535) query string.\n`matches` should be a regex. Supports regexes validated by the\n[`fancy-regex`](https://docs.rs/fancy-regex/latest/fancy_regex/) crate\n\nExample:\n```json\n\"http_filter\": {\n \"body_filter\": {\n \"body\": \"json\",\n \"query\": \"$.library.books[*]\",\n \"matches\": \"^\\\\d{3,5}$\"\n }\n}\n```\nwill match\n```json\n{\n \"library\": {\n \"books\": [\n 34555,\n 1233,\n 234\n 23432\n ]\n }\n}\n```\n\nThe filter will match if there is at least one query result.\n\nNon-string matches are stringified before being compared to\nthe regex. To filter query results by type, the `typeof`\n[function extension](https://www.rfc-editor.org/rfc/rfc9535.html#name-function-extensions)\nis provided. It takes in a single `NodesType` parameter and\nreturns `\"null\" | \"bool\" | \"number\" | \"string\" | \"array\" | \"object\"`,\ndepending on the type of the argument. If not all nodes in the\nargument have the same type, it returns `nothing`.\n\nExample:\n\n```json\n\"body_filter\": {\n \"body\": \"json\",\n \"query\": \"$.books[?(typeof(@) == 'number')]\",\n \"matches\": \"4$\"\n}\n```\nwill match\n\n```json\n{\n \"books\": [\n 1111,\n 2222,\n 4444\n ]\n}\n```\n\nbut not\n\n```json\n{\n \"books\": [\n \"1111\",\n \"2222\",\n \"4444\"\n ]\n}\n```\n\n\n\nTo use with `all_of` or `any_of`, use the following syntax:\n```json\n\"http_filter\": {\n \"all_of\": [\n {\n \"path\": \"/buildings\"\n },\n {\n \"body\": \"json\",\n \"query\": \"$.library.books[*]\",\n \"matches\": \"^\\\\d{3,5}$\"\n }\n ]\n}\n```",
"type": "object",
"properties": {
"body": {
"type": "string",
"const": "json"
},
"matches": {
"type": "string"
},
"query": {
"type": "string"
}
},
"additionalProperties": false,
"required": [
"body",
"query",
"matches"
]
}
]
},
"BranchItemCopyConfig": {
"description": "Shared copy config for individual items (tables, collections, etc.).\nAll database engines use this same struct for per-item copy configuration.",
"type": "object",
"properties": {
"filter": {
"type": [
"string",
"null"
]
}
},
"additionalProperties": false
},
"CiFileConfig": {
"description": "Configuration for mirrord for CI.\n\n```json\n{\n \"ci\": {\n \"output_dir\": \"/tmp/mirrord/\",\n }\n}\n```",
"type": "object",
"properties": {
"output_dir": {
"title": "ci.output_dir {#ci-output_dir}",
"description": "Path to a directory where `mirrord ci` will flush application's stdout and stderr.\n\nDefaults to `/tmp/mirrord/`.",
"type": [
"string",
"null"
]
}
},
"additionalProperties": false
},
"ClickhouseBranchCopyConfig": {
"description": "Users can choose from the following copy mode to bootstrap their ClickHouse branch database:\n\n- Empty\n\n Creates an empty database. If the source DB connection options are found from the chosen\n target, mirrord operator extracts the database name and create an empty DB. Otherwise, mirrord\n operator looks for the `name` field from the branch DB config object. This option is useful\n for users that run DB migrations themselves before starting the application.\n\n- Schema\n\n Creates an empty database and copies the schema of all tables.\n\n- All\n\n Copies both schema and data of all tables. This option shall only be used\n when the data volume of the source database is minimal.\n\nIn `empty` and `schema` mode, a per-table `filter` (a ClickHouse `WHERE` clause) copies just the\nmatching rows of the listed tables.",
"oneOf": [
{
"type": "object",
"properties": {
"mode": {
"type": "string",
"const": "empty"
},
"tables": {
"type": [
"object",
"null"
],
"additionalProperties": {
"$ref": "#/$defs/BranchItemCopyConfig"
}
}
},
"additionalProperties": false,
"required": [
"mode"
]
},
{
"type": "object",
"properties": {
"mode": {
"type": "string",
"const": "schema"
},
"tables": {
"type": [
"object",
"null"
],
"additionalProperties": {
"$ref": "#/$defs/BranchItemCopyConfig"
}
}
},
"additionalProperties": false,
"required": [
"mode"
]
},
{
"type": "object",
"properties": {
"mode": {
"type": "string",
"const": "all"
}
},
"additionalProperties": false,
"required": [
"mode"
]
}
]
},
"CockroachdbBranchCopyConfig": {
"description": "Users can choose from the following copy mode to bootstrap their CockroachDB branch database:\n\n- Empty\n\n Creates an empty database. If the source DB connection options are found from the chosen\n target, mirrord operator extracts the database name and create an empty DB. Otherwise, mirrord\n operator looks for the `name` field from the branch DB config object. This option is useful\n for users that run DB migrations themselves before starting the application.\n\n- Schema\n\n Creates an empty database and copies schema of all tables via `SHOW CREATE ALL TABLES`.\n\n- All\n\n Copies both schema and data of all tables. This option shall only be used\n when the data volume of the source database is minimal.",
"oneOf": [
{
"type": "object",
"properties": {
"mode": {
"type": "string",
"const": "empty"
},
"tables": {
"type": [
"object",
"null"
],
"additionalProperties": {
"$ref": "#/$defs/BranchItemCopyConfig"
}
}
},
"additionalProperties": false,
"required": [
"mode"
]
},
{
"type": "object",
"properties": {
"mode": {
"type": "string",
"const": "schema"
},
"tables": {
"type": [
"object",
"null"
],
"additionalProperties": {
"$ref": "#/$defs/BranchItemCopyConfig"
}
}
},
"additionalProperties": false,
"required": [
"mode"
]
},
{
"type": "object",
"properties": {
"mode": {
"type": "string",
"const": "all"
}
},
"additionalProperties": false,
"required": [
"mode"
]
}
]
},
"ConcurrentSteal": {
"description": "(Operator Only): Allows overriding port locks\n\nCan be set to either `\"continue\"` or `\"override\"`.\n\n- `\"continue\"`: Continue with normal execution\n- `\"override\"`: If port lock detected then override it with new lock and force close the\n original locking connection.",
"oneOf": [
{
"description": "<!--${internal}-->\n### override\n\nOverride any port lock and force close the original lock connection",
"type": "string",
"const": "override"
},
{
"description": "<!--${internal}-->\n### continue\n\nContinue with normal execution",
"type": "string",
"const": "continue"
},
{
"description": "<!--${internal}-->\n### abort\n\nAbort Execution when trying to steal traffic from a target whose traffic is already being\nstolen.",
"type": "string",
"const": "abort"
}
]
},
"ConfigMount": {
"description": "A single file to project into the preview pod's container filesystem.\n\nEither `payload`+`type` (inline) or `from_file` (load from disk) must\nbe set; the two forms are mutually exclusive. After\n[`ConfigMount::resolve`] has been called, the result is guaranteed\nto have `payload` and `r#type` set and `from_file` cleared. This form\ngets sent to the operator.",
"type": "object",
"properties": {
"from_file": {
"description": "Local filesystem path to read the file from. The contents are loaded\nat session creation time and sent as `\"text\"` (verbatim) if valid\nUTF-8, otherwise base64-encoded and sent as `\"binary\"`. Mutually\nexclusive with `payload`.",
"type": [
"string",
"null"
]
},
"mount_at": {
"description": "Absolute path inside the preview pod's container where the file should\nappear. The surrounding directory's other contents\n(if any) are left untouched, only this one path is overlaid.",
"type": "string"
},
"payload": {
"description": "Inline file contents. Interpretation depends on `type`: for `\"text\"`,\nwritten to the file byte-for-byte; for `\"binary\"`, base64-decoded\nfirst. Mutually exclusive with `from_file`.",
"type": [
"string",
"null"
]
},
"type": {
"description": "How the `payload` payload should be interpreted when writing it to the\nfile. `\"text\"` for verbatim UTF-8 content, `\"binary\"` for\nbase64-encoded bytes. Required when `payload` is set; determined\nautomatically when `from_file` is used.",
"anyOf": [
{
"$ref": "#/$defs/ConfigMountType"
},
{
"type": "null"
}
]
}
},
"required": [
"mount_at"
]
},
"ConfigMountType": {
"description": "Encoding of a [`ConfigMount::payload`] payload.",
"oneOf": [
{
"description": "`payload` is written to the file verbatim. Used for\nhuman-readable config files (YAML, TOML, JSON, env files,\nshell scripts, ...).",
"type": "string",
"const": "text"
},
{
"description": "`payload` is base64-encoded and is decoded before being\nwritten. Used for binary files that can't safely live in a\nJSON string.",
"type": "string",
"const": "binary"
}
]
},
"ConnectionParamsConfig": {
"description": "Connection parameters specified as individual environment variable names.\nThe `type` field is optional - when omitted, the operator auto-detects\nwhether the variable comes from `env` or `envFrom` on the target pod.",
"type": "object",
"properties": {
"params": {
"$ref": "#/$defs/ConnectionParamsVars"
},
"type": {
"anyOf": [
{
"$ref": "#/$defs/ConnectionSourceType"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false,
"required": [
"params"
]
},
"ConnectionParamsVars": {
"description": "Individual database connection parameter sources.\nAt least one parameter must be specified.\nEach parameter is either a plain string (env var name) or an object with `secret` and `key`.",
"type": "object",
"properties": {
"database": {
"anyOf": [
{
"$ref": "#/$defs/SingleOrVec_ParamSource"
},
{
"type": "null"
}
]
},
"host": {
"anyOf": [
{
"$ref": "#/$defs/SingleOrVec_ParamSource"
},
{
"type": "null"
}
]
},
"password": {
"anyOf": [
{
"$ref": "#/$defs/SingleOrVec_ParamSource"
},
{
"type": "null"
}
]
},
"port": {
"anyOf": [
{
"$ref": "#/$defs/SingleOrVec_ParamSource"
},
{
"type": "null"
}
]
},
"url": {
"anyOf": [
{
"$ref": "#/$defs/SingleOrVec_ParamSource"
},
{
"type": "null"
}
]
},
"user": {
"anyOf": [
{
"$ref": "#/$defs/SingleOrVec_ParamSource"
},
{
"type": "null"
}
]
}
},
"additionalProperties": {
"$ref": "#/$defs/SingleOrVec_ParamSource"
}
},
"ConnectionSourceType": {
"description": "The type of environment variable source for connection params.",
"type": "string",
"enum": [
"env",
"env_from"
]
},
"ContainerFileConfig": {
"description": "Unstable: `mirrord container` command specific config.",
"type": "object",
"properties": {
"cli_extra_args": {
"title": "container.cli_extra_args {#container-cli_extra_args}",
"description": "Any extra args to use when creating the sidecar mirrord-cli container.\n\nThis is useful when you want to use portforwarding, passing `-p local:container` won't work\nfor main command but adding them here will work\n```json\n{\n \"container\": {\n \"cli_extra_args\": [\"-p\", \"local:container\"]\n }\n}\n```",
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
},
"cli_image": {
"title": "container.cli_image {#container-cli_image}",
"description": "Tag of the `mirrord-cli` image you want to use.\n\nDefaults to `\"ghcr.io/metalbear-co/mirrord-cli:<cli version>\"`.",
"type": [
"string",
"null"
]
},
"cli_image_lib_path": {
"title": "container.cli_image_lib_path {#container-cli_image}",
"description": "Path of the mirrord-layer lib inside the specified mirrord-cli image.",
"type": [
"string",
"null"
]
},
"cli_prevent_cleanup": {
"title": "container.cli_prevent_cleanup {#container-cli_extra_args}",
"description": "Don't add `--rm` to sidecar command to prevent cleanup.",
"type": [
"boolean",
"null"
]
},
"cli_tls_path": {
"title": "container.cli_tls_path {#container-cli_tls_path}",
"description": "When using`mirrord container` with external_proxy TLS enabled (is enabled by default), you\ncan specify the path where the certificate `.pem` file will be created, in the cli\ncontainer.\n\nDefaults to `\"/opt/mirrord/tls/mirrord-tls.pem\"`.",
"type": [
"string",
"null"
]
},
"host_gateway_detection": {
"title": "container.host_gateway_detection {#container-host_gateway_detection}",
"description": "Connects the internal proxy sidecar to the external proxy through the container\nruntime's host-gateway mechanism, instead of dialing a detected host IP.\n\nThe sidecar container is started with `--add-host mirrord-external-proxy:host-gateway`,\nand the internal proxy connects to that hostname. The external proxy also binds all\ninterfaces by default so it's reachable through the container runtime's gateway.\n\nWhen enabled, `container.override_host_ip` and the WSL-specific auto-adjustment of\n`external_proxy.host_ip` are no longer needed and are ignored.",
"type": [
"boolean",
"null"
]
},
"override_host_ip": {
"title": "container.override_host_ip {#container-override_host_ip}",
"description": "Allows to override the IP address for the internal proxy to use\nwhen connecting to the host machine from within the container.\n\n```json5\n{\n \"container\": {\n \"override_host_ip\": \"172.17.0.1\" // usual resolution of value from `host.docker.internal`\n }\n}\n```\n\nThis should be useful if your host machine is exposed with a different IP address than the\none bound as host.\n\n- If you're running inside WSL, and encountering problems, try setting\n `external_proxy.host_ip` to `0.0.0.0`, and this to the internal container runtime address\n (for docker, this would be what `host.docker.internal` resolved to, which by default is\n `192.168.65.254`). You can find this ip by resolving it from inside a running container,\n e.g. `docker run --rm -it {image-with-nslookup} nslookup host.docker.internal`",
"type": [
"string",
"null"
],
"format": "ip"
},
"platform": {
"title": "container.platform {#container-platform}",
"description": "Platform specification for the target container (e.g., \"linux/amd64\", \"linux/arm64\").\n\nWhen specified, the target container will run with this platform, while the internal proxy\ncontainer will still run on the native platform and contain both architectures (x64/arm64).\nThe LD_PRELOAD will automatically use the correct architecture.\n\n```json\n{\n \"container\": {\n \"platform\": \"linux/amd64\"\n }\n}\n```",
"type": [
"string",
"null"
]
}
},
"additionalProperties": false
},
"ContainerRuntime": {
"description": "Container runtimes supported by mirrord.",
"oneOf": [
{
"description": "Docker container runtime. (default)",
"type": "string",
"const": "docker"
},
{
"description": "Podman container runtime.",
"type": "string",
"const": "podman"
},
{
"description": "nerdctl container runtime (containerd).",
"type": "string",
"const": "nerdctl"
}
]
},
"CopyTargetFileConfig": {
"title": "feature.copy_target {#copy_target}",
"description": "Allows the user to target a pod created dynamically from the original [`target`](#target).\nThe new pod inherits most of the original target's specification, e.g. labels.\n\nSee the [copy target reference](https://metalbear.com/mirrord/docs/reference/copy-target/)\nfor more details.\n\n### Minimal `copy_target` config {#copy_target-minimal}\n\n```json\n{\n \"feature\": {\n \"copy_target\": true\n }\n}\n```\n\n### Advanced `copy_target` config {#copy_target-advanced}\n\n```json\n{\n \"feature\": {\n \"copy_target\": {\n \"enabled\": true,\n \"scale_down\": true,\n \"exclude_containers\": [\"my-container\"],\n \"exclude_init_containers\": [\"my-init-container\"]\n }\n }\n}\n```",
"anyOf": [
{
"description": "Basic configuration that controls whether copy target is enabled (default false).",
"type": "boolean"
},
{
"description": "Allows the user to specify both enabling copy target and additional configuration options.",
"type": "object",
"properties": {
"enabled": {
"description": "Whether copy target is enabled",
"type": [
"boolean",
"null"
]
},
"exclude_containers": {
"description": "List of containers to be ignored by copy_target",
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
},
"exclude_init_containers": {
"description": "List of init containers to be ignored by copy_target",
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
},
"scale_down": {
"description": "Scale down the target deployment to 0 for the time the copied pod is alive",
"type": [
"boolean",
"null"
]
}
},
"additionalProperties": false
}
]
},
"CronJobTarget": {
"type": "object",
"properties": {
"container": {
"type": [
"string",
"null"
]
},
"cron_job": {
"type": "string"
}
},
"additionalProperties": false,
"required": [
"cron_job"
]
},
"DatabaseBranchConfig": {
"description": "Configuration for a database branch.\n\nExample:\n\n```json\n{\n \"id\": \"my-branch-db\",\n \"name\": \"my-database-name\",\n \"ttl_secs\": 120,\n \"type\": \"mysql\",\n \"version\": \"8.0\",\n \"connection\": {\n \"url\": {\n \"type\": \"env\",\n \"variable\": \"DB_CONNECTION_URL\"\n }\n }\n}\n```\n\nThe fields below are shared by every engine. Not every engine has every one of them: an\nengine that mirrord does not spawn as a pod in the cluster takes no `image`/`version`, and\none that is not reached over a connection to a server hosting many databases takes no\n`name` and locates its source its own way. Engine-specific fields (copy modes, `iam_auth`,\n`connection_settings`, `emulator_host`) are documented under each `type`.\n\n#### feature.db_branches[].id (type: mysql, mariadb, pg, mongodb, mssql, redis) {#feature-db_branches-sql-id}\n\nUsers can choose to specify a unique `id`. This is useful for reusing or sharing\nthe same database branch among Kubernetes users.\n\n#### feature.db_branches[].name (type: mysql, mariadb, pg, mongodb, mssql, redis) {#feature-db_branches-sql-name}\n\nWhen source database connection detail is not accessible to mirrord operator, users\ncan specify the database `name` so it is included in the connection options mirrord\nuses as the override.\n\n#### feature.db_branches[].ttl_secs (type: mysql, mariadb, pg, mongodb, mssql, redis) {#feature-db_branches-sql-ttl_secs}\n\nMirrord operator starts counting the TTL when a branch is no longer used by any session.\nThe time-to-live (TTL) for the branch database is set to 300 seconds by default.\nUsers can set `ttl_secs` to customize this value according to their need. Please note\nthat longer TTL paired with frequent mirrord session turnover can result in increased\nresource usage. For this reason, branch database TTL caps out at 15 min.\n\nMutually exclusive with [`ttl_mins`](#feature-db_branches-sql-ttl_mins).\n\n#### feature.db_branches[].ttl_mins (type: mysql, mariadb, pg, mongodb, mssql, redis) {#feature-db_branches-sql-ttl_mins}\n\nSame as [`ttl_secs`](#feature-db_branches-sql-ttl_secs) but expressed in minutes.\n\nMutually exclusive with [`ttl_secs`](#feature-db_branches-sql-ttl_secs).\n\n#### feature.db_branches[].creation_timeout_secs (type: mysql, mariadb, pg, mongodb, mssql, redis) {#feature-db_branches-sql-creation_timeout_secs}\n\nThe timeout in seconds to wait for a database branch to become ready after creation.\nDefaults to 60 seconds. Adjust this value based on your database size and cluster\nperformance.\n\n#### feature.db_branches[].version (type: mysql, mariadb, pg, mongodb, mssql, redis) {#feature-db_branches-sql-version}\n\nMirrord operator uses a default version of the database image unless `version` is given.\n\nMutually exclusive with [`image`](#feature-db_branches-sql-image).\n\n#### feature.db_branches[].image (type: mysql, mariadb, pg, mongodb, mssql, redis) {#feature-db_branches-sql-image}\n\nFull image reference for the branch database container, including the tag\n(e.g. `registry.example.com/postgresql:15-partman`). Setting `image` overrides both the\noperator's built-in default image and any registry configured cluster-wide by the operator\nadmin. Cluster admins can restrict which images are accepted with the per-database\n`dbPod.allowedImages` list in the operator's Helm values; when that list is not set, any\nimage is allowed.\n\nMutually exclusive with [`version`](#feature-db_branches-sql-version), as the image\nreference already carries the tag.\n\n#### feature.db_branches[].profile (type: clickhouse, cockroachdb, dynamodb, generic, mariadb, mongodb, mssql, mysql, pg, redis, s3, spanner) {#feature-db_branches-sql-profile}\n\nName of an operator branch-config profile to use for this branch. Cluster admins can define\nnamed profiles under the per-database `profiles` map in the operator's Helm values\n(e.g. `redisBranchConfig.profiles`), each carrying its own pod settings such as TLS mode,\nserver arguments, pull secrets, and allowed images. When `profile` is not set, the\noperator's default branch config applies. Referencing a profile the operator does not\ndefine fails the branch with an error listing the available profiles.\n\n```json\n{ \"type\": \"redis\", \"profile\": \"telapp\", \"connection\": { \"url\": \"REDIS_URL\" } }\n```\n\n#### feature.db_branches[].connection (type: mysql, mariadb, pg, mongodb, mssql, redis) {#feature-db_branches-sql-connection}\n\n`connection` describes how to get the connection information to the source database.\nWhen the branch database is ready for use, Mirrord operator will replace the connection\ninformation with the branch database's. It accepts a connection URL or individual params:\n\n```json\n{ \"url\": { \"type\": \"env\", \"variable\": \"DB_CONNECTION_URL\" } }\n```\n```json\n{ \"type\": \"env\", \"url\": \"DB_CONNECTION_URL\" }\n```\n```json\n{ \"type\": \"env\", \"params\": { \"host\": \"DB_HOST\", \"port\": \"DB_PORT\", \"user\": \"DB_USER\", \"password\": \"DB_PASSWORD\", \"database\": \"DB_NAME\" } }\n```\n\nAny param can also be read from a Kubernetes Secret instead of a target-pod env var:\n\n```json\n{ \"type\": \"env\", \"params\": { \"host\": \"DB_HOST\", \"password\": { \"secret\": \"my-secret\", \"key\": \"password\" }, \"database\": \"DB_NAME\" } }\n```\n\nOr from a Kubernetes ConfigMap, for apps whose connection details live in a mounted config\nfile. `configmap` is the ConfigMap's name, or `{ \"volume\": \"<name>\" }` to follow a `configMap`\nvolume of the target pod (this keeps working when the ConfigMap is renamed per release).\n`key` is the data key; `value_selector` (a `.a.b` path into the JSON/YAML entry) or\n`value_pattern` (a regex over the raw text) picks the value out of it; `env_var_name` hands\nthe branch's value to the local app under that name:\n\n```json\n{\n \"params\": {\n \"host\": { \"configmap\": { \"volume\": \"app-config\" }, \"key\": \"config.yml\", \"value_selector\": \".database.host\", \"env_var_name\": \"DB_HOST\" },\n \"database\": { \"configmap\": \"app-config\", \"key\": \"config.yml\", \"value_pattern\": \"name: '(?P<database>[^']+)'\", \"env_var_name\": \"DB_NAME\" },\n \"user\": \"DB_USER\",\n \"password\": \"DB_PASSWORD\"\n }\n}\n```\n\nWhen the operator's branch config (or the branch's `profile`) sets `dbPod.sourceConfigMap`,\n`configmap` and `key` may be omitted and are filled from there, so a param is just its\nselector: `{ \"value_selector\": \".database.host\", \"env_var_name\": \"DB_HOST\" }`. A param with\nonly `value_pattern` and `env_var_name` is the env var pattern source instead, so a pattern\nagainst the profile's ConfigMap keeps `key`. ConfigMap sources need operator `3.204.0` and\nmirrord `3.255.0` or later.\n\n#### feature.db_branches[].migrations (type: mysql, mariadb, pg, mssql, clickhouse) {#feature-db_branches-sql-migrations}\n\nSchema migrations to run on the branch after it is created. The `flavor` field selects how\nthey run.\n\n[Flyway](https://documentation.red-gate.com/flyway) with a local migrations directory:\n\n```json\n{ \"migrations\": { \"flavor\": \"flyway\", \"path\": \"./migrations\" } }\n```\n\n- `path`: local directory holding the migration files, resolved relative to the working\n directory.\n- `image`: optional container image override for the migration runner.\n\nFlyway with the SQL baked into the job image, running against in-image paths:\n\n```json\n{\n \"migrations\": {\n \"flavor\": \"flyway\",\n \"image\": \"registry.example.com/my-migrations:latest\",\n \"locations\": [\"filesystem:/flyway/sql\"]\n }\n}\n```\n\n- `locations`: Flyway locations inside `image` holding the migration files. Mutually exclusive\n with `path`, and requires `image`.\n\n[Liquibase](https://docs.liquibase.com) with a local changelog directory:\n\n```json\n{\n \"migrations\": {\n \"flavor\": \"liquibase\",\n \"path\": \"./changelog\",\n \"changelog_file\": \"db.changelog-master.xml\"\n }\n}\n```\n\n- `path`: local directory holding the changelog files, resolved relative to the working\n directory.\n- `changelog_file`: root changelog file, relative to `path`.\n- `image`: optional container image override for the migration runner.\n\nLiquibase with the changelogs baked into the job image, running against in-image paths:\n\n```json\n{\n \"migrations\": {\n \"flavor\": \"liquibase\",\n \"image\": \"registry.example.com/my-migrations:latest\",\n \"search_path\": [\"/liquibase/changelog\"],\n \"changelog_file\": \"db.changelog-master.xml\"\n }\n}\n```\n\n- `search_path`: Liquibase search path inside `image`. Mutually exclusive with `path`, and\n requires `image`.\n\n`changelog_file` is resolved inside the search root - a leading `/` is accepted and normalised\nto the same name - and is recorded in `DATABASECHANGELOG`, so changing it re-runs every\nchangeset.\n\nA user-provided image and command, for apps that ship migrations in their own image\n(e.g. a setup script that runs the framework's migration command):\n\n```json\n{\n \"migrations\": {\n \"flavor\": \"container\",\n \"image\": \"registry.example.com/my-app:latest\",\n \"command\": [\"./db_setup.sh\"],\n \"env\": {\n \"DATABASE_URL\": \"mysql://$(MIRRORD_DB_USER):$(MIRRORD_DB_PASSWORD)@$(MIRRORD_DB_HOST):$(MIRRORD_DB_PORT)/$(MIRRORD_DB_NAME)\"\n }\n }\n}\n```\n\n- `image`: full image reference for the migration container, including the tag.\n- `command`/`args`: optional entrypoint overrides; when unset, the image's own entrypoint runs.\n- `env`: extra environment variables. The operator injects the branch connection as\n `MIRRORD_DB_HOST`, `MIRRORD_DB_PORT`, `MIRRORD_DB_USER`, `MIRRORD_DB_PASSWORD`, and\n `MIRRORD_DB_NAME`; `env` values (and `command`/`args`) can reference them with Kubernetes\n `$(VAR)` expansion.\n\nRequires [`name`](#feature-db_branches-sql-name) to be set.",
"oneOf": [
{
"description": "When configuring a branch for ClickHouse, set `type` to `clickhouse`.",
"type": "object",
"properties": {
"connection": {
"description": "How to source the connection info for the source database. The operator swaps it for\nthe branch's connection once the branch is ready.",
"$ref": "#/$defs/DbBranchingConnectionSource"
},
"copy": {
"$ref": "#/$defs/ClickhouseBranchCopyConfig",
"default": {
"mode": "empty",
"tables": null
}
},
"creation_timeout_secs": {
"description": "Seconds to wait for a branch to become ready after creation. Defaults to 60.",
"type": "integer",
"format": "uint64",
"default": 60,
"minimum": 0
},
"id": {
"description": "Optional stable id for reusing or sharing a branch across users.",
"type": [
"string",
"null"
]
},
"image": {
"description": "Full image reference for the branch container, including the tag. Overrides the\noperator-configured registry entirely. Mutually exclusive with `version`.",
"type": [
"string",
"null"
]
},
"name": {
"description": "Source database name, used when the operator cannot read it from the connection.",
"type": [
"string",
"null"
]
},
"profile": {
"description": "Name of an admin-defined operator branch-config profile to use for this branch.",
"type": [
"string",
"null"
]
},
"ttl_mins": {
"description": "Branch TTL in minutes, counted from when the branch is last used. Mutually exclusive\nwith `ttl_secs`.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
},
"ttl_secs": {
"description": "Branch TTL in seconds, counted from when the branch is last used. Mutually exclusive\nwith `ttl_mins`. Defaults to 300, capped at 15 minutes.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
},
"type": {
"type": "string",
"const": "clickhouse"
},
"version": {
"description": "Source database image version. Defaults to the operator's built-in version.",
"type": [
"string",
"null"
]
}
},
"additionalProperties": false,
"required": [
"type",
"connection"
]
},
{
"description": "When configuring a branch for CockroachDB, set `type` to `cockroachdb`.\n\nCockroachDB is PostgreSQL-wire-compatible, so the app connects to the branch with its\nexisting PostgreSQL driver. The branch itself is a `cockroachdb/cockroach` single node and\nthe copy uses CockroachDB-native tooling (`cockroach sql`, `SHOW CREATE ALL TABLES`,\n`COPY ... TO/FROM STDOUT/STDIN WITH CSV`) rather than the PostgreSQL dump tools.",
"type": "object",
"properties": {
"connection": {
"description": "How to source the connection info for the source database. The operator swaps it for\nthe branch's connection once the branch is ready.",
"$ref": "#/$defs/DbBranchingConnectionSource"
},
"copy": {
"$ref": "#/$defs/CockroachdbBranchCopyConfig",
"default": {
"mode": "empty",
"tables": null
}
},
"creation_timeout_secs": {
"description": "Seconds to wait for a branch to become ready after creation. Defaults to 60.",
"type": "integer",
"format": "uint64",
"default": 60,
"minimum": 0
},
"id": {
"description": "Optional stable id for reusing or sharing a branch across users.",
"type": [
"string",
"null"
]
},
"image": {
"description": "Full image reference for the branch container, including the tag. Overrides the\noperator-configured registry entirely. Mutually exclusive with `version`.",
"type": [
"string",
"null"
]
},
"migrations": {
"description": "<!--${internal}-->\nDocumented on `DatabaseBranchConfig` (shared across SQL engines).",
"anyOf": [
{
"$ref": "#/$defs/SqlBranchMigrationsConfig"
},
{
"type": "null"
}
]
},
"name": {
"description": "Source database name, used when the operator cannot read it from the connection.",
"type": [
"string",
"null"
]
},
"profile": {
"description": "Name of an admin-defined operator branch-config profile to use for this branch.",
"type": [
"string",
"null"
]
},
"ttl_mins": {
"description": "Branch TTL in minutes, counted from when the branch is last used. Mutually exclusive\nwith `ttl_secs`.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
},
"ttl_secs": {
"description": "Branch TTL in seconds, counted from when the branch is last used. Mutually exclusive\nwith `ttl_mins`. Defaults to 300, capped at 15 minutes.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
},
"type": {
"type": "string",
"const": "cockroachdb"
},
"version": {
"description": "Source database image version. Defaults to the operator's built-in version.",
"type": [
"string",
"null"
]
}
},
"additionalProperties": false,
"required": [
"type",
"connection"
]
},
{
"description": "When configuring a branch for DynamoDB, set `type` to `dynamodb`.",
"type": "object",
"properties": {
"connection": {
"description": "How to source the connection info for the source database. The operator swaps it for\nthe branch's connection once the branch is ready.",
"$ref": "#/$defs/DbBranchingConnectionSource"
},
"copy": {
"$ref": "#/$defs/DynamodbBranchCopyConfig",
"default": {
"collections": null,
"mode": "empty"
}
},
"creation_timeout_secs": {
"description": "Seconds to wait for a branch to become ready after creation. Defaults to 60.",
"type": "integer",
"format": "uint64",
"default": 60,
"minimum": 0
},
"iam_auth": {
"title": "feature.db_branches[].iam_auth (type: dynamodb) {#feature-db_branches-dynamodb-iam_auth}",
"description": "AWS IAM credentials used to read the source DynamoDB tables. DynamoDB has no\npassword-based connection mode, so this is the only way to authenticate against the\nsource when `copy.mode` is `all`.",
"anyOf": [
{
"$ref": "#/$defs/IamAuthConfig"
},
{
"type": "null"
}
]
},
"id": {
"description": "Optional stable id for reusing or sharing a branch across users.",
"type": [
"string",
"null"
]
},
"image": {
"description": "Full image reference for the branch container, including the tag. Overrides the\noperator-configured registry entirely. Mutually exclusive with `version`.",
"type": [
"string",
"null"
]
},
"name": {
"description": "Source database name, used when the operator cannot read it from the connection.",
"type": [
"string",
"null"
]
},
"profile": {
"description": "Name of an admin-defined operator branch-config profile to use for this branch.",
"type": [
"string",
"null"
]
},
"ttl_mins": {
"description": "Branch TTL in minutes, counted from when the branch is last used. Mutually exclusive\nwith `ttl_secs`.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
},
"ttl_secs": {
"description": "Branch TTL in seconds, counted from when the branch is last used. Mutually exclusive\nwith `ttl_mins`. Defaults to 300, capped at 15 minutes.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
},
"type": {
"type": "string",
"const": "dynamodb"
},
"version": {
"description": "Source database image version. Defaults to the operator's built-in version.",
"type": [
"string",
"null"
]
}
},
"additionalProperties": false,
"required": [
"type",
"connection"
]
},
{
"description": "When branching a database, cache, or any other stateful service that mirrord has no built-in\nengine for, set `type` to `generic`.\n\nA generic branch runs the container image you supply, starting empty - there are no built-in\ncopy modes, IAM auth, or schema handling. The operator resolves each parameter you declare\nunder `connection.params` from the target pod and injects it into the branch container as an\nenv var named `MIRRORD_PARAM_<NAME>` (a Secret-backed param arrives as a `secretKeyRef`; the\noperator never reads its value). Reference these in `command`, `args`, and `env` with\nKubernetes' native `$(VAR)` syntax so the branch bootstraps itself with the *same* values the\napp already uses. mirrord then only redirects the app's `host`/`port` vars to the branch -\neverything else in the app's environment keeps working unchanged.\n\nTo fill the branch with schema and data, supply a `copy` Job (below) - your own image that\ncopies from the source into the branch. The branch only turns Ready once the Job succeeds.\n\nTwo built-ins are always available alongside the params: `MIRRORD_BRANCH_ID` and, when the\nshared `name` field is set, `MIRRORD_DATABASE_NAME`.\n\n#### feature.db_branches[].image (type: generic) {#feature-db_branches-generic-image}\n\nFull image reference for the branch container, including the tag\n(e.g. `docker.io/library/influxdb:2.7`). This is the shared `image` field, but unlike the\nbuilt-in engines a generic branch has no default image, so it is required - unless the\nshared `profile` field is set and the admin's profile supplies the image (the operator\nfails the branch if neither does). The shared `version` field is not allowed for generic\nbranches - the tag lives here.\n\n#### feature.db_branches[].port (type: generic) {#feature-db_branches-generic-port}\n\nThe port the branched service listens on. Used as the default readiness probe target and as\nthe port the app's connection is redirected to. Required, unless the shared `profile` field\nis set and the admin's profile supplies the port.\n\n#### feature.db_branches[].copy (type: generic) {#feature-db_branches-generic-copy}\n\nA one-shot Kubernetes Job that copies schema and data from the source into the branch,\nusing an image you author. It runs after the branch container turns ready, and the branch\nonly becomes usable once the Job exits successfully. The Job's environment carries the same\n`MIRRORD_PARAM_<NAME>` vars as the branch container (the source connection), plus\n`MIRRORD_BRANCH_HOST` and `MIRRORD_BRANCH_PORT` (where to write). What to copy\n(full/schema-only/filtered) is entirely up to the image's own command.\n\nThe copy runs at most once per branch: reusing a Ready branch (by `id`) never re-runs it,\neven if the copy config changed - use a new `id` for a fresh copy.\n\n```json\n{\n \"copy\": {\n \"image\": \"ghcr.io/acme/opensearch-copy:1.0\",\n \"command\": [\"./copy.sh\"],\n \"args\": [\"--mode=all\"]\n }\n}\n```\n\nWhen the shared `profile` field is set, the admin's profile can supply the copy Job instead\n(`dbPod.copy` in the operator's generic branch config); a `copy` here wins over the\nprofile's.\n\n#### feature.db_branches[].command / args (type: generic) {#feature-db_branches-generic-command}\n\nOptional entrypoint override for the branch container. Values may reference declared params\nwith `$(MIRRORD_PARAM_<NAME>)`; use `$$(...)` for a literal `$(...)`. Values written\ndirectly into `args` are visible in the pod spec - reference params via `$(VAR)` instead of\ninlining secrets.\n\n#### feature.db_branches[].env (type: generic) {#feature-db_branches-generic-env}\n\nExtra environment variables for the branch container. Values support the same\n`$(MIRRORD_PARAM_<NAME>)` references as `command`/`args`. Keys must not start with\n`MIRRORD_PARAM_` or collide with the built-ins.\n\n#### feature.db_branches[].readiness (type: generic) {#feature-db_branches-generic-readiness}\n\nReadiness check for the branch container. Defaults to a TCP probe on `port`.\n\n```json\n{ \"readiness\": { \"type\": \"http_get\", \"path\": \"/health\" } }\n```\n```json\n{ \"readiness\": { \"type\": \"exec\", \"command\": [\"redis-cli\", \"ping\"] } }\n```\n\nExample - an empty InfluxDB branch, bootstrapped with the app's own token/org/bucket so the\napp's untouched credential vars stay valid against the branch:\n\n```json\n{\n \"type\": \"generic\",\n \"id\": \"my-influx-branch\",\n \"image\": \"docker.io/library/influxdb:2.7\",\n \"port\": 8086,\n \"connection\": {\n \"params\": {\n \"host\": { \"env_var_name\": \"INFLUXDB_URL\", \"value_pattern\": \"https?://([^:/]+)\" },\n \"port\": { \"env_var_name\": \"INFLUXDB_URL\", \"value_pattern\": \":([0-9]+)\" },\n \"token\": { \"secret\": \"influx-creds\", \"key\": \"token\" },\n \"org\": \"INFLUXDB_ORG\",\n \"bucket\": \"INFLUXDB_BUCKET\"\n }\n },\n \"env\": {\n \"DOCKER_INFLUXDB_INIT_MODE\": \"setup\",\n \"DOCKER_INFLUXDB_INIT_USERNAME\": \"admin\",\n \"DOCKER_INFLUXDB_INIT_PASSWORD\": \"mirrord-branch\",\n \"DOCKER_INFLUXDB_INIT_ADMIN_TOKEN\": \"$(MIRRORD_PARAM_TOKEN)\",\n \"DOCKER_INFLUXDB_INIT_ORG\": \"$(MIRRORD_PARAM_ORG)\",\n \"DOCKER_INFLUXDB_INIT_BUCKET\": \"$(MIRRORD_PARAM_BUCKET)\"\n }\n}\n```\n\n`token`, `org`, and `bucket` are custom params - any key works under `connection.params`,\nnot just the fixed `host`/`port`/`user`/`password`/`database` slots. The connection must use\nparams mode (URL-shaped env vars are covered by `value_pattern` on `host`/`port`), and\n`gcp_secret_manager`/`aws_secrets_manager` sources are not supported for generic branches.",
"type": "object",
"properties": {
"args": {
"description": "Entrypoint args override for the branch container.",
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
},
"command": {
"description": "Entrypoint command override for the branch container.",
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
},
"connection": {
"description": "How to source the connection info for the source database. The operator swaps it for\nthe branch's connection once the branch is ready.",
"$ref": "#/$defs/DbBranchingConnectionSource"
},
"copy": {
"description": "One-shot Job that copies schema/data from the source into the branch before it turns\nReady.",
"anyOf": [
{
"$ref": "#/$defs/GenericCopyConfig"
},
{
"type": "null"
}
]
},
"creation_timeout_secs": {
"description": "Seconds to wait for a branch to become ready after creation. Defaults to 60.",
"type": "integer",
"format": "uint64",
"default": 60,
"minimum": 0
},
"env": {
"description": "Extra environment variables for the branch container.",
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"id": {
"description": "Optional stable id for reusing or sharing a branch across users.",
"type": [
"string",
"null"
]
},
"image": {
"description": "Full image reference for the branch container, including the tag. Overrides the\noperator-configured registry entirely. Mutually exclusive with `version`.",
"type": [
"string",
"null"
]
},
"name": {
"description": "Source database name, used when the operator cannot read it from the connection.",
"type": [
"string",
"null"
]
},
"port": {
"description": "The port the branched service listens on. Required unless `profile` is set and the\nprofile supplies it.",
"type": [
"integer",
"null"
],
"format": "uint16",
"maximum": 65535,
"minimum": 0
},
"profile": {
"description": "Name of an admin-defined operator branch-config profile to use for this branch.",
"type": [
"string",
"null"
]
},
"readiness": {
"description": "Readiness check for the branch container. Defaults to a TCP probe on `port`.",
"anyOf": [
{
"$ref": "#/$defs/GenericReadinessConfig"
},
{
"type": "null"
}
]
},
"ttl_mins": {
"description": "Branch TTL in minutes, counted from when the branch is last used. Mutually exclusive\nwith `ttl_secs`.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
},
"ttl_secs": {
"description": "Branch TTL in seconds, counted from when the branch is last used. Mutually exclusive\nwith `ttl_mins`. Defaults to 300, capped at 15 minutes.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
},
"type": {
"type": "string",
"const": "generic"
},
"version": {
"description": "Source database image version. Defaults to the operator's built-in version.",
"type": [
"string",
"null"
]
}
},
"additionalProperties": false,
"required": [
"type",
"connection"
]
},
{
"description": "When configuring a branch for MariaDB, set `type` to `mariadb`.\n\nMariaDB branches use MariaDB-native tooling (`mariadb-dump` / `mariadb-admin`) and a\nMariaDB 12 branch image, so MariaDB-only objects such as sequences and system-versioned\n(temporal) tables are copied correctly - which a MySQL dump/image mishandles.",
"type": "object",
"properties": {
"connection": {
"description": "How to source the connection info for the source database. The operator swaps it for\nthe branch's connection once the branch is ready.",
"$ref": "#/$defs/DbBranchingConnectionSource"
},
"copy": {
"$ref": "#/$defs/MariadbBranchCopyConfig",
"default": {
"mode": "empty",
"tables": null
}
},
"creation_timeout_secs": {
"description": "Seconds to wait for a branch to become ready after creation. Defaults to 60.",
"type": "integer",
"format": "uint64",
"default": 60,
"minimum": 0
},
"iam_auth": {
"title": "feature.db_branches[].iam_auth (type: mariadb) {#feature-db_branches-mariadb-iam_auth}",
"description": "IAM authentication for the source database.\nUse this when your source database (AWS RDS, GCP Cloud SQL) requires IAM authentication\ninstead of password-based authentication.",
"anyOf": [
{
"$ref": "#/$defs/IamAuthConfig"
},
{
"type": "null"
}
]
},
"id": {
"description": "Optional stable id for reusing or sharing a branch across users.",
"type": [
"string",
"null"
]
},
"image": {
"description": "Full image reference for the branch container, including the tag. Overrides the\noperator-configured registry entirely. Mutually exclusive with `version`.",
"type": [
"string",
"null"
]
},
"migrations": {
"description": "<!--${internal}-->\nDocumented on `DatabaseBranchConfig` (shared across SQL engines).",
"anyOf": [
{
"$ref": "#/$defs/SqlBranchMigrationsConfig"
},
{
"type": "null"
}
]
},
"name": {
"description": "Source database name, used when the operator cannot read it from the connection.",
"type": [
"string",
"null"
]
},
"profile": {
"description": "Name of an admin-defined operator branch-config profile to use for this branch.",
"type": [
"string",
"null"
]
},
"ttl_mins": {
"description": "Branch TTL in minutes, counted from when the branch is last used. Mutually exclusive\nwith `ttl_secs`.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
},
"ttl_secs": {
"description": "Branch TTL in seconds, counted from when the branch is last used. Mutually exclusive\nwith `ttl_mins`. Defaults to 300, capped at 15 minutes.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
},
"type": {
"type": "string",
"const": "mariadb"
},
"version": {
"description": "Source database image version. Defaults to the operator's built-in version.",
"type": [
"string",
"null"
]
}
},
"additionalProperties": false,
"required": [
"type",
"connection"
]
},
{
"description": "When configuring a branch for MongoDB, set `type` to `mongodb`.",
"type": "object",
"properties": {
"connection": {
"description": "How to source the connection info for the source database. The operator swaps it for\nthe branch's connection once the branch is ready.",
"$ref": "#/$defs/DbBranchingConnectionSource"
},
"copy": {
"$ref": "#/$defs/MongodbBranchCopyConfig",
"default": {
"collections": null,
"mode": "empty"
}
},
"creation_timeout_secs": {
"description": "Seconds to wait for a branch to become ready after creation. Defaults to 60.",
"type": "integer",
"format": "uint64",
"default": 60,
"minimum": 0
},
"iam_auth": {
"title": "feature.db_branches[].iam_auth (type: mongodb) {#feature-db_branches-mongodb-iam_auth}",
"description": "IAM authentication for the source database.\nUse this when your source database (e.g. MongoDB Atlas with AWS IAM) requires the\n`MONGODB-AWS` authentication mechanism instead of password-based authentication.\nOnly the `aws_rds` type is supported for MongoDB.",
"anyOf": [
{
"$ref": "#/$defs/IamAuthConfig"
},
{
"type": "null"
}
]
},
"id": {
"description": "Optional stable id for reusing or sharing a branch across users.",
"type": [
"string",
"null"
]
},
"image": {
"description": "Full image reference for the branch container, including the tag. Overrides the\noperator-configured registry entirely. Mutually exclusive with `version`.",
"type": [
"string",
"null"
]
},
"name": {
"description": "Source database name, used when the operator cannot read it from the connection.",
"type": [
"string",
"null"
]
},
"profile": {
"description": "Name of an admin-defined operator branch-config profile to use for this branch.",
"type": [
"string",
"null"
]
},
"ttl_mins": {
"description": "Branch TTL in minutes, counted from when the branch is last used. Mutually exclusive\nwith `ttl_secs`.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
},
"ttl_secs": {
"description": "Branch TTL in seconds, counted from when the branch is last used. Mutually exclusive\nwith `ttl_mins`. Defaults to 300, capped at 15 minutes.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
},
"type": {
"type": "string",
"const": "mongodb"
},
"version": {
"description": "Source database image version. Defaults to the operator's built-in version.",
"type": [
"string",
"null"
]
}
},
"additionalProperties": false,
"required": [
"type",
"connection"
]
},
{
"description": "When configuring a branch for MSSQL, set `type` to `mssql`.",
"type": "object",
"properties": {
"connection": {
"description": "How to source the connection info for the source database. The operator swaps it for\nthe branch's connection once the branch is ready.",
"$ref": "#/$defs/DbBranchingConnectionSource"
},
"copy": {
"$ref": "#/$defs/MssqlBranchCopyConfig",
"default": {
"mode": "empty",
"tables": null
}
},
"creation_timeout_secs": {
"description": "Seconds to wait for a branch to become ready after creation. Defaults to 60.",
"type": "integer",
"format": "uint64",
"default": 60,
"minimum": 0
},
"id": {
"description": "Optional stable id for reusing or sharing a branch across users.",
"type": [
"string",
"null"
]
},
"image": {
"description": "Full image reference for the branch container, including the tag. Overrides the\noperator-configured registry entirely. Mutually exclusive with `version`.",
"type": [
"string",
"null"
]
},
"migrations": {
"description": "<!--${internal}-->\nDocumented on `DatabaseBranchConfig` (shared across SQL engines).",
"anyOf": [
{
"$ref": "#/$defs/SqlBranchMigrationsConfig"
},
{
"type": "null"
}
]
},
"name": {
"description": "Source database name, used when the operator cannot read it from the connection.",
"type": [
"string",
"null"
]
},
"profile": {
"description": "Name of an admin-defined operator branch-config profile to use for this branch.",
"type": [
"string",
"null"
]
},
"ttl_mins": {
"description": "Branch TTL in minutes, counted from when the branch is last used. Mutually exclusive\nwith `ttl_secs`.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
},
"ttl_secs": {
"description": "Branch TTL in seconds, counted from when the branch is last used. Mutually exclusive\nwith `ttl_mins`. Defaults to 300, capped at 15 minutes.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
},
"type": {
"type": "string",
"const": "mssql"
},
"version": {
"description": "Source database image version. Defaults to the operator's built-in version.",
"type": [
"string",
"null"
]
}
},
"additionalProperties": false,
"required": [
"type",
"connection"
]
},
{
"description": "When configuring a branch for MySQL, set `type` to `mysql`.",
"type": "object",
"properties": {
"connection": {
"description": "How to source the connection info for the source database. The operator swaps it for\nthe branch's connection once the branch is ready.",
"$ref": "#/$defs/DbBranchingConnectionSource"
},
"copy": {
"$ref": "#/$defs/MysqlBranchCopyConfig",
"default": {
"mode": "empty",
"tables": null
}
},
"creation_timeout_secs": {
"description": "Seconds to wait for a branch to become ready after creation. Defaults to 60.",
"type": "integer",
"format": "uint64",
"default": 60,
"minimum": 0
},
"iam_auth": {
"title": "feature.db_branches[].iam_auth (type: mysql) {#feature-db_branches-mysql-iam_auth}",
"description": "IAM authentication for the source database.\nUse this when your source database (AWS RDS, GCP Cloud SQL) requires IAM authentication\ninstead of password-based authentication.",
"anyOf": [
{
"$ref": "#/$defs/IamAuthConfig"
},
{
"type": "null"
}
]
},
"id": {
"description": "Optional stable id for reusing or sharing a branch across users.",
"type": [
"string",
"null"
]
},
"image": {
"description": "Full image reference for the branch container, including the tag. Overrides the\noperator-configured registry entirely. Mutually exclusive with `version`.",
"type": [
"string",
"null"
]
},
"migrations": {
"description": "<!--${internal}-->\nDocumented on `DatabaseBranchConfig` (shared across SQL engines).",
"anyOf": [
{
"$ref": "#/$defs/SqlBranchMigrationsConfig"
},
{
"type": "null"
}
]
},
"name": {
"description": "Source database name, used when the operator cannot read it from the connection.",
"type": [
"string",
"null"
]
},
"profile": {
"description": "Name of an admin-defined operator branch-config profile to use for this branch.",
"type": [
"string",
"null"
]
},
"ttl_mins": {
"description": "Branch TTL in minutes, counted from when the branch is last used. Mutually exclusive\nwith `ttl_secs`.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
},
"ttl_secs": {
"description": "Branch TTL in seconds, counted from when the branch is last used. Mutually exclusive\nwith `ttl_mins`. Defaults to 300, capped at 15 minutes.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
},
"type": {
"type": "string",
"const": "mysql"
},
"version": {
"description": "Source database image version. Defaults to the operator's built-in version.",
"type": [
"string",
"null"
]
}
},
"additionalProperties": false,
"required": [
"type",
"connection"
]
},
{
"description": "When configuring a branch for PostgreSQL, set `type` to `pg`.",
"type": "object",
"properties": {
"additional_databases": {
"title": "feature.db_branches[].additional_databases (type: pg) {#feature-db_branches-pg-additional_databases}",
"description": "More databases from the same source server, copied into the same branch server. Use\nit when your application talks to several databases on one PostgreSQL server: every\ndatabase lands on one branch pod, so the application keeps using a single host.\n\nEach entry is dumped with the source connection of this branch (same host, port,\nuser, password, TLS, `iam_auth` and `connection_settings`); only the database name\ndiffers. The database gets the same name on the branch.\n\n- `name`: the database name on the source server. Must differ from the branch's own\n database and from every other entry.\n- `connection` (optional): how the application reaches this database, in the same shape as\n [`connection`](#feature-db_branches-sql-connection). mirrord points it at the branch with\n this database's name. Without it, the database is only created and copied, and the\n application switches to it on the branch host by itself.\n- `copy` (optional): copy mode and table filters for this database, in the same shape as\n the branch's own `copy`. Defaults to `empty`.\n\n```json\n{\n \"feature\": {\n \"db_branches\": [\n {\n \"type\": \"pg\",\n \"connection\": { \"url\": { \"type\": \"env\", \"variable\": \"DATABASE_URL\" } },\n \"copy\": { \"mode\": \"all\" },\n \"additional_databases\": [\n {\n \"name\": \"analytics\",\n \"connection\": { \"url\": { \"type\": \"env\", \"variable\": \"ANALYTICS_DATABASE_URL\" } },\n \"copy\": { \"mode\": \"schema\" }\n }\n ]\n }\n ]\n }\n}\n```",
"type": "array",
"items": {
"$ref": "#/$defs/PgAdditionalDatabaseConfig"
}
},
"connection": {
"description": "How to source the connection info for the source database. The operator swaps it for\nthe branch's connection once the branch is ready.",
"$ref": "#/$defs/DbBranchingConnectionSource"
},
"connection_settings": {
"title": "feature.db_branches[].connection_settings (type: pg) {#feature-db_branches-pg-connection_settings}",
"description": "PostgreSQL settings (GUCs) applied to every source connection mirrord opens while\nbuilding the branch. Each entry is sent at connection startup via `PGOPTIONS`, so it\nis in effect before any schema dump or data copy runs.\n\nThe common use is a Row-Level Security tenant variable: if a source table has an RLS\npolicy that reads `current_setting('my.tenant')`, set `{ \"my.tenant\": \"1234\" }` here so\nthe copy can read the rows. Other GUCs work too, e.g. `role` to assume a table owner, or\n`search_path`.\n\nValues are literal strings; the usual config templating (such as `{{ get_env(...) }}`)\nstill applies before they are sent.",
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"copy": {
"$ref": "#/$defs/PgBranchCopyConfig",
"default": {
"mode": "empty",
"tables": null
}
},
"creation_timeout_secs": {
"description": "Seconds to wait for a branch to become ready after creation. Defaults to 60.",
"type": "integer",
"format": "uint64",
"default": 60,
"minimum": 0
},
"iam_auth": {
"title": "feature.db_branches[].iam_auth (type: pg) {#feature-db_branches-pg-iam_auth}",
"description": "IAM authentication for the source database.\nUse this when your source database (AWS RDS, GCP Cloud SQL) requires IAM authentication\ninstead of password-based authentication.",
"anyOf": [
{
"$ref": "#/$defs/IamAuthConfig"
},
{
"type": "null"
}
]
},
"id": {
"description": "Optional stable id for reusing or sharing a branch across users.",
"type": [
"string",
"null"
]
},
"image": {
"description": "Full image reference for the branch container, including the tag. Overrides the\noperator-configured registry entirely. Mutually exclusive with `version`.",
"type": [
"string",
"null"
]
},
"migrations": {
"description": "<!--${internal}-->\nDocumented on `DatabaseBranchConfig` (shared across SQL engines).",
"anyOf": [
{
"$ref": "#/$defs/SqlBranchMigrationsConfig"
},
{
"type": "null"
}
]
},
"name": {
"description": "Source database name, used when the operator cannot read it from the connection.",
"type": [
"string",
"null"
]
},
"profile": {
"description": "Name of an admin-defined operator branch-config profile to use for this branch.",
"type": [
"string",
"null"
]
},
"query_params": {
"title": "feature.db_branches[].query_params (type: pg) {#feature-db_branches-pg-query_params}",
"description": "Query parameters applied to the branch connection handed to your application - the\nreconstructed connection URL and, in params mode, the matching environment variables.\nValues win over mirrord's own defaults. They only affect the branch connection; the\nsource database connection used for the copy is not changed.\n\nThe common use is `sslmode`: a source like GCP Cloud SQL may require\n`?sslmode=require`, while the branch pod mirrord creates serves no TLS, so the branch\nconnection needs `{ \"sslmode\": \"disable\" }` (this is also mirrord's default for\nnon-TLS branch pods).\n\n```json\n{\n \"feature\": {\n \"db_branches\": [\n {\n \"type\": \"pg\",\n \"query_params\": { \"sslmode\": \"disable\" }\n }\n ]\n }\n}\n```",
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"ttl_mins": {
"description": "Branch TTL in minutes, counted from when the branch is last used. Mutually exclusive\nwith `ttl_secs`.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
},
"ttl_secs": {
"description": "Branch TTL in seconds, counted from when the branch is last used. Mutually exclusive\nwith `ttl_mins`. Defaults to 300, capped at 15 minutes.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
},
"type": {
"type": "string",
"const": "pg"
},
"version": {
"description": "Source database image version. Defaults to the operator's built-in version.",
"type": [
"string",
"null"
]
}
},
"additionalProperties": false,
"required": [
"type",
"connection"
]
},
{
"description": "When configuring a branch for Redis, set `type` to `redis`.",
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "redis"
}
},
"oneOf": [
{
"description": "Configuration for a local Redis branch.\n\nExample with URL-based connection:\n```json\n{\n \"type\": \"redis\",\n \"location\": \"local\",\n \"connection\": {\n \"url\": {\n \"type\": \"env\",\n \"variable\": \"REDIS_URL\"\n }\n }\n}\n```\n\nExample with separated settings:\n```json\n{\n \"type\": \"redis\",\n \"location\": \"local\",\n \"connection\": {\n \"host\": { \"type\": \"env\", \"variable\": \"REDIS_HOST\" },\n \"port\": 6379,\n \"password\": { \"type\": \"env\", \"variable\": \"REDIS_PASSWORD\" }\n }\n}\n```",
"type": "object",
"properties": {
"connection": {
"title": "feature.db_branches[].connection (type: redis) {#feature-db_branches-redis-connection}",
"description": "Connection configuration for the Redis instance.",
"$ref": "#/$defs/RedisConnectionConfig",
"default": {
"database": null,
"host": null,
"password": null,
"port": null,
"tls": null,
"url": null,
"username": null
}
},
"id": {
"title": "feature.db_branches[].id (type: redis) {#feature-db_branches-redis-id}",
"description": "Optional unique identifier for reusing branches across sessions.",
"type": [
"string",
"null"
],
"default": null
},
"local": {
"title": "feature.db_branches[].local (type: redis) {#feature-db_branches-redis-local}",
"description": "Local Redis runtime configuration.",
"$ref": "#/$defs/RedisLocalConfig",
"default": {
"container_command": null,
"container_runtime": "docker",
"options": {
"args": []
},
"port": 6379,
"runtime": "container",
"server_command": null,
"version": "7-alpine"
}
},
"location": {
"description": "#### feature.db_branches[].location (type: redis) {#feature-db_branches-redis-location}\nWhere the Redis instance should run.\n- `local`: Spawns a local instance.\n- `remote`: Uses a remote instance (default).",
"type": "string",
"const": "local"
}
}
},
{
"description": "Configuration for a remote Redis branch.",
"type": "object",
"properties": {
"connection": {
"description": "How to source the connection info for the source database. The operator swaps it for\nthe branch's connection once the branch is ready.",
"$ref": "#/$defs/DbBranchingConnectionSource"
},
"copy": {
"title": "feature.db_branches[].copy (type: redis) {#feature-db_branches-redis-copy}",
"description": "How a Redis branch is seeded from its source.",
"$ref": "#/$defs/RedisBranchCopyConfig",
"default": {
"mode": "empty"
}
},
"creation_timeout_secs": {
"description": "Seconds to wait for a branch to become ready after creation. Defaults to 60.",
"type": "integer",
"format": "uint64",
"default": 60,
"minimum": 0
},
"id": {
"description": "Optional stable id for reusing or sharing a branch across users.",
"type": [
"string",
"null"
]
},
"image": {
"description": "Full image reference for the branch container, including the tag. Overrides the\noperator-configured registry entirely. Mutually exclusive with `version`.",
"type": [
"string",
"null"
]
},
"location": {
"description": "#### feature.db_branches[].location (type: redis) {#feature-db_branches-redis-location}\nWhere the Redis instance should run.\n- `local`: Spawns a local instance.\n- `remote`: Uses a remote instance (default).",
"type": "string",
"const": "remote"
},
"name": {
"description": "Source database name, used when the operator cannot read it from the connection.",
"type": [
"string",
"null"
]
},
"profile": {
"description": "Name of an admin-defined operator branch-config profile to use for this branch.",
"type": [
"string",
"null"
]
},
"ttl_mins": {
"description": "Branch TTL in minutes, counted from when the branch is last used. Mutually exclusive\nwith `ttl_secs`.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
},
"ttl_secs": {
"description": "Branch TTL in seconds, counted from when the branch is last used. Mutually exclusive\nwith `ttl_mins`. Defaults to 300, capped at 15 minutes.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
},
"version": {
"description": "Source database image version. Defaults to the operator's built-in version.",
"type": [
"string",
"null"
]
}
},
"required": [
"connection"
]
}
],
"required": [
"type"
]
},
{
"description": "When configuring a branch for an object storage bucket, set `type` to `s3`.\n\nThe branch bucket is created and seeded by the provider's own API, so - unlike the engines\nmirrord runs as database servers - an S3 branch has no pod in the cluster and takes no\n`image` nor `version`. It is otherwise a normal branch: the standard `id`,\n`ttl_secs`/`ttl_mins`, `creation_timeout_secs` and `profile` options all apply.\n\nThe source bucket is located with a `bucket` purum naming the env var on the target that holds\nthe bucket name. Once the branch bucket exists, the operator points that same variable at it,\nso the app reads and writes the branch instead of the real bucket with no code change.\n\nExample:\n```json\n{\n \"type\": \"s3\",\n \"provider\": \"AWS\",\n \"source\": {\n \"params\": {\n \"bucket\": \"MY_BUCKET_ENV_VAR\"\n }\n },\n \"copy\": { \"mode\": \"all\", \"objects\": [\"^fixtures/.*\"] }\n}\n```",
"type": "object",
"properties": {
"copy": {
"title": "feature.db_branches[].copy (type: s3) {#feature-db_branches-s3-copy}",
"description": "How the branch bucket is seeded from the source bucket.",
"$ref": "#/$defs/S3BranchCopyConfig",
"default": {
"mode": "empty"
}
},
"creation_timeout_secs": {
"description": "Seconds to wait for a branch to become ready after creation. Defaults to 60.",
"type": "integer",
"format": "uint64",
"default": 60,
"minimum": 0
},
"id": {
"description": "Optional stable id for reusing or sharing a branch across users.",
"type": [
"string",
"null"
]
},
"profile": {
"description": "Name of an admin-defined operator branch-config profile to use for this branch.",
"type": [
"string",
"null"
]
},
"provider": {
"title": "feature.db_branches[].provider (type: s3) {#feature-db_branches-s3-provider}",
"description": "Cloud provider hosting the source bucket. Defaults to `AWS`.",
"$ref": "#/$defs/S3Provider",
"default": "AWS"
},
"source": {
"title": "feature.db_branches[].source (type: s3) {#feature-db_branches-s3-source}",
"description": "Where to read the source bucket's name from, in the same params shape the other engines\nuse for their connection details - `type` picks how the variables are resolved on the\ntarget (`env` for the pod spec's own `env`, `env_from` for its `envFrom` sources), and is\nauto-detected when omitted.\n\nThe single param an S3 branch takes is `bucket`:\n\n```json\n{ \"source\": { \"params\": { \"bucket\": \"MY_BUCKET_ENV_VAR\" } } }\n```\n\n```json\n{ \"source\": { \"type\": \"env_from\", \"params\": { \"bucket\": \"MY_BUCKET_ENV_VAR\" } } }\n```\n\nIts value is as flexible as any other engine's params: a Kubernetes Secret\n(`{ \"secret\": \"my-secret\", \"key\": \"bucket\" }`), a literal\n(`{ \"variable\": \"MY_BUCKET_ENV_VAR\", \"value\": \"my-bucket\" }`), or a regex extracting the\nname out of a larger variable (`{ \"env_var_name\": \"S3_URI\", \"value_pattern\": \"...\" }`).",
"$ref": "#/$defs/ConnectionParamsConfig"
},
"ttl_mins": {
"description": "Branch TTL in minutes, counted from when the branch is last used. Mutually exclusive\nwith `ttl_secs`.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
},
"ttl_secs": {
"description": "Branch TTL in seconds, counted from when the branch is last used. Mutually exclusive\nwith `ttl_mins`. Defaults to 300, capped at 15 minutes.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
},
"type": {
"type": "string",
"const": "s3"
}
},
"additionalProperties": false,
"required": [
"type",
"source"
]
},
{
"description": "When configuring a branch for Google Cloud Spanner, set `type` to `spanner`.\n\nThe branch runs the Cloud Spanner emulator. mirrord redirects the app to it by injecting\n`SPANNER_EMULATOR_HOST`, so the app's own `project`/`instance`/`database` values keep working\nand now resolve against the emulator. The branch init sidecar recreates the matching instance\nand database in the emulator and, for `schema`/`all`, copies from the real Spanner using the\ntarget pod's Google service account (Workload Identity / Application Default Credentials).\n\nSpanner's source identifiers live flat under `connection.params`: `project`, `instance`, and\n`database_id` each name the env var (on the target pod) that holds the value. They are read\nonly - the app never has them overridden - so they belong with the connection source rather\nthan at the top level. `database_id` (not `database`) keeps the locator from colliding with the\nfixed `database` connection slot. `emulator_host` is the exception: it is the var mirrord *sets*\non the local process to point at the branch, so it stays top-level.\n\nExample:\n```json\n{\n \"type\": \"spanner\",\n \"connection\": {\n \"params\": {\n \"project\": \"GOOGLE_CLOUD_PROJECT\",\n \"instance\": \"SPANNER_INSTANCE_ID\",\n \"database_id\": \"SPANNER_DATABASE_ID\"\n }\n },\n \"copy\": { \"mode\": \"schema\" }\n}\n```",
"type": "object",
"properties": {
"connection": {
"description": "How to source the connection info for the source database. The operator swaps it for\nthe branch's connection once the branch is ready.",
"$ref": "#/$defs/DbBranchingConnectionSource"
},
"copy": {
"$ref": "#/$defs/SpannerBranchCopyConfig",
"default": {
"mode": "empty",
"tables": null
}
},
"creation_timeout_secs": {
"description": "Seconds to wait for a branch to become ready after creation. Defaults to 60.",
"type": "integer",
"format": "uint64",
"default": 60,
"minimum": 0
},
"emulator_host": {
"title": "feature.db_branches[].emulator_host (type: spanner) {#feature-db_branches-spanner-emulator_host}",
"description": "The *name* of the env var mirrord sets on your process to redirect it to the branch\nemulator - not an address. mirrord fills that var's *value* with the branch emulator's\n`host:port` at runtime; you only choose which variable it writes to.\n\nDefaults to `SPANNER_EMULATOR_HOST`, the variable every official Spanner client library\nchecks on its own, so the default redirects the app with no code change. Leave it unset\nunless your app ignores that built-in detection and reads the emulator endpoint from a\ndifferently-named variable (for example it reads `MY_EMULATOR` and passes that endpoint to\nthe client explicitly); then set this to that variable's name so mirrord writes the address\nwhere your app actually looks.",
"type": "string",
"default": "SPANNER_EMULATOR_HOST"
},
"id": {
"description": "Optional stable id for reusing or sharing a branch across users.",
"type": [
"string",
"null"
]
},
"image": {
"description": "Full image reference for the branch container, including the tag. Overrides the\noperator-configured registry entirely. Mutually exclusive with `version`.",
"type": [
"string",
"null"
]
},
"name": {
"description": "Source database name, used when the operator cannot read it from the connection.",
"type": [
"string",
"null"
]
},
"profile": {
"description": "Name of an admin-defined operator branch-config profile to use for this branch.",
"type": [
"string",
"null"
]
},
"ttl_mins": {
"description": "Branch TTL in minutes, counted from when the branch is last used. Mutually exclusive\nwith `ttl_secs`.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
},
"ttl_secs": {
"description": "Branch TTL in seconds, counted from when the branch is last used. Mutually exclusive\nwith `ttl_mins`. Defaults to 300, capped at 15 minutes.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
},
"type": {
"type": "string",
"const": "spanner"
},
"version": {
"description": "Source database image version. Defaults to the operator's built-in version.",
"type": [
"string",
"null"
]
}
},
"additionalProperties": false,
"required": [
"type",
"connection"
]
},
{
"description": "When configuring a branch for a turbopuffer namespace, set `type` to `turbopuffer`.\n\nThe branch namespace is a copy-on-write clone made by turbopuffer itself, so - like an S3\nbranch - it has no pod in the cluster and takes no `image` nor `version`. It is otherwise\na normal branch: the standard `id`, `ttl_secs`/`ttl_mins` and `creation_timeout_secs`\noptions all apply.\n\nturbopuffer clients pick the namespace per request, so the app has to read the namespace\nname from an env var for mirrord to redirect it. The `namespace` param names that var;\nonce the branch exists, the operator points it at the branch namespace. The `api_key`\nparam names where the operator reads the API key it branches with, and `region` (or\n`base_url`) where the namespace lives. Those two are only read, never rewritten.\n\nExample:\n```json\n{\n \"type\": \"turbopuffer\",\n \"source\": {\n \"params\": {\n \"namespace\": \"TPUF_NAMESPACE\",\n \"api_key\": \"TURBOPUFFER_API_KEY\",\n \"region\": { \"env_var_name\": \"TURBOPUFFER_REGION\", \"value\": \"gcp-us-central1\" }\n }\n },\n \"copy\": { \"mode\": \"all\" }\n}\n```",
"type": "object",
"properties": {
"copy": {
"title": "feature.db_branches[].copy (type: turbopuffer) {#feature-db_branches-turbopuffer-copy}",
"description": "How the branch namespace is seeded from the source namespace.",
"$ref": "#/$defs/TurbopufferBranchCopyConfig",
"default": {
"mode": "empty"
}
},
"creation_timeout_secs": {
"description": "Seconds to wait for a branch to become ready after creation. Defaults to 60.",
"type": "integer",
"format": "uint64",
"default": 60,
"minimum": 0
},
"id": {
"description": "Optional stable id for reusing or sharing a branch across users.",
"type": [
"string",
"null"
]
},
"profile": {
"description": "Name of an admin-defined operator branch-config profile to use for this branch.",
"type": [
"string",
"null"
]
},
"source": {
"title": "feature.db_branches[].source (type: turbopuffer) {#feature-db_branches-turbopuffer-source}",
"description": "Where to read the source namespace, the API key and the region from, in the same\nparams shape the other engines use for their connection details - `type` picks how\nthe variables are resolved on the target (`env` for the pod spec's own `env`,\n`env_from` for its `envFrom` sources), and is auto-detected when omitted.\n\nThe params a turbopuffer branch takes:\n\n- `namespace` (required): the env var holding the source namespace name. The operator\n rewrites it to the branch namespace, so it must name an env var.\n- `api_key` (required): the API key used to branch and later delete the namespace.\n- `region`: the turbopuffer region, e.g. `gcp-us-central1`.\n- `base_url`: the full API endpoint, for dedicated clusters. Exactly one of `region` and\n `base_url` must be set.\n\n```json\n{\n \"source\": {\n \"params\": {\n \"namespace\": \"TPUF_NAMESPACE\",\n \"api_key\": { \"secret\": \"turbopuffer\", \"key\": \"api-key\" },\n \"base_url\": \"TURBOPUFFER_BASE_URL\"\n }\n }\n}\n```\n\nEach value is as flexible as any other engine's params: a Kubernetes Secret\n(`{ \"secret\": \"my-secret\", \"key\": \"namespace\" }`), a literal\n(`{ \"env_var_name\": \"TURBOPUFFER_REGION\", \"value\": \"gcp-us-central1\" }`), or a regex\nextracting the name out of a larger variable\n(`{ \"env_var_name\": \"TPUF_URI\", \"value_pattern\": \"...\" }`).",
"$ref": "#/$defs/ConnectionParamsConfig"
},
"ttl_mins": {
"description": "Branch TTL in minutes, counted from when the branch is last used. Mutually exclusive\nwith `ttl_secs`.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
},
"ttl_secs": {
"description": "Branch TTL in seconds, counted from when the branch is last used. Mutually exclusive\nwith `ttl_mins`. Defaults to 300, capped at 15 minutes.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
},
"type": {
"type": "string",
"const": "turbopuffer"
}
},
"additionalProperties": false,
"required": [
"type",
"source"
]
}
]
},
"DatabaseBranchesConfig": {
"description": "A list of configurations for database branches.\n\nUsing a connection URL:\n```json\n{\n \"feature\": {\n \"db_branches\": [\n {\n \"type\": \"mysql\",\n \"connection\": {\n \"url\": { \"type\": \"env\", \"variable\": \"DB_CONNECTION_URL\" }\n }\n }\n ]\n }\n}\n```\n\nUsing individual connection params:\n```json\n{\n \"feature\": {\n \"db_branches\": [\n {\n \"type\": \"mysql\",\n \"connection\": {\n \"type\": \"env\",\n \"params\": { \"host\": \"DB_HOST\", \"port\": \"DB_PORT\", \"database\": \"DB_NAME\" }\n }\n }\n ]\n }\n}\n```",
"type": "array",
"items": {
"$ref": "#/$defs/DatabaseBranchConfig"
}
},
"DbBranchingConnectionSource": {
"description": "Different ways of connecting to the source database.\n\nAccepts three formats:\n\nLegacy URL (backward compatible):\n```json\n{ \"url\": { \"type\": \"env\", \"variable\": \"DB_CONNECTION_URL\" } }\n```\n\nFlat URL:\n```json\n{ \"type\": \"env\", \"url\": \"DB_CONNECTION_URL\" }\n```\n\nIndividual connection params:\n```json\n{ \"type\": \"env\", \"params\": { \"host\": \"DB_HOST\", \"port\": \"DB_PORT\", \"user\": \"DB_USER\", \"password\": \"DB_PASSWORD\", \"database\": \"DB_NAME\" } }\n```\n\nIndividual connection params with password from a Kubernetes Secret:\n```json\n{ \"type\": \"env\", \"params\": { \"host\": \"DB_HOST\", \"password\": { \"secret\": \"my-secret\", \"key\": \"password\" }, \"database\": \"DB_NAME\" } }\n```\n\nIndividual connection params read from a ConfigMap, for apps whose connection details live\nin a mounted config file. `configmap` is the ConfigMap's name, or `{ \"volume\": ... }` to\nfollow a `configMap` volume of the target pod (survives ConfigMaps renamed per release).\n`key` is the data key; `value_selector` (a `.a.b` path into the JSON/YAML entry) or\n`value_pattern` (a regex over the raw text) picks the value out of it, and `env_var_name`\nhands the branch's value to the local app under that name:\n```json\n{\n \"params\": {\n \"host\": { \"configmap\": { \"volume\": \"app-config\" }, \"key\": \"config.yml\", \"value_selector\": \".database.host\", \"env_var_name\": \"DB_HOST\" },\n \"database\": { \"configmap\": \"app-config\", \"key\": \"config.yml\", \"value_pattern\": \"name: '(?P<database>[^']+)'\", \"env_var_name\": \"DB_NAME\" },\n \"user\": \"DB_USER\",\n \"password\": \"DB_PASSWORD\"\n }\n}\n```\n\nWhen the operator's branch config (or the branch's `profile`) sets `dbPod.sourceConfigMap`,\n`configmap` and `key` may be omitted and are filled from there, so a param can be just the\nselector. A param with only `value_pattern` and `env_var_name` is the env var pattern\nsource instead, so a pattern against the profile's ConfigMap keeps `key`:\n```json\n{ \"params\": { \"host\": { \"value_selector\": \".database.host\", \"env_var_name\": \"DB_HOST\" } } }\n```",
"anyOf": [
{
"type": "object",
"properties": {
"url": {
"$ref": "#/$defs/DbBranchingConnectionSourceKind"
}
},
"additionalProperties": false,
"required": [
"url"
]
},
{
"type": "object",
"properties": {
"type": {
"anyOf": [
{
"$ref": "#/$defs/ConnectionSourceType"
},
{
"type": "null"
}
]
},
"url": {
"$ref": "#/$defs/SingleOrVec_string"
}
},
"additionalProperties": false,
"required": [
"url"
]
},
{
"$ref": "#/$defs/ConnectionParamsConfig"
}
]
},
"DbBranchingConnectionSourceKind": {
"description": "<!--${internal}-->\nDifferent ways to source the connection options.\n\nSupport:\n- `env` in the target's pod spec.\n- `envFrom` in the target's pod spec.\n- `secret` read directly from a Kubernetes Secret.\n- `gcp_secret_manager` fetched from Google Secret Manager by the init container.\n- `aws_secrets_manager` fetched from AWS Secrets Manager by the init container.",
"oneOf": [
{
"type": "object",
"properties": {
"container": {
"type": [
"string",
"null"
]
},
"type": {
"type": "string",
"const": "env"
},
"value": {
"description": "Literal value for this connection parameter. The CLI sends it to the\noperator, which stores it in a Kubernetes Secret for the branch pod.",
"type": [
"string",
"null"
]
},
"variable": {
"type": "string"
}
},
"additionalProperties": false,
"required": [
"type",
"variable"
]
},
{
"type": "object",
"properties": {
"container": {
"type": [
"string",
"null"
]
},
"type": {
"type": "string",
"const": "env_from"
},
"variable": {
"type": "string"
}
},
"additionalProperties": false,
"required": [
"type",
"variable"
]
},
{
"type": "object",
"properties": {
"env_var_name": {
"description": "Name of the env var to set on the local process from the resolved\nSecret value. Same semantics as on `ParamSource::Secret`.",
"type": [
"string",
"null"
]
},
"key": {
"type": "string"
},
"name": {
"type": "string"
},
"type": {
"type": "string",
"const": "secret"
}
},
"additionalProperties": false,
"required": [
"type",
"name",
"key"
]
},
{
"description": "Fetched from Google Secret Manager by the branch init container using the\ntarget pod's service account. Same semantics as `ParamSource::GcpSecretManager`.",
"type": "object",
"properties": {
"env_var_name": {
"type": [
"string",
"null"
]
},
"secret_ref": {
"type": "string"
},
"type": {
"type": "string",
"const": "gcp_secret_manager"
}
},
"additionalProperties": false,
"required": [
"type",
"secret_ref"
]
},
{
"description": "Fetched from AWS Secrets Manager by the branch init container using the\ntarget pod's service account. Same semantics as `ParamSource::AwsSecretsManager`.",
"type": "object",
"properties": {
"env_var_name": {
"type": [
"string",
"null"
]
},
"secret_ref": {
"type": "string"
},
"type": {
"type": "string",
"const": "aws_secrets_manager"
}
},
"additionalProperties": false,
"required": [
"type",
"secret_ref"
]
}
]
},
"DeploymentTarget": {
"description": "<!--${internal}-->\nMirror the deployment specified by [`DeploymentTarget::deployment`].",
"type": "object",
"properties": {
"container": {
"type": [
"string",
"null"
]
},
"deployment": {
"description": "<!--${internal}-->\nDeployment to mirror.",
"type": "string"
}
},
"additionalProperties": false,
"required": [
"deployment"
]
},
"DnsFileConfig": {
"description": "Resolve DNS via the remote pod.\n\nDefaults to `true`.\n\nMind that:\n- DNS resolving can be done in multiple ways. Some frameworks use `getaddrinfo`/`gethostbyname`\n functions, while others communicate directly with the DNS server at port `53` and perform a\n sort of manual resolution. Just enabling the `dns` feature in mirrord might not be enough. If\n you see an address resolution error, try enabling the [`fs`](#feature-fs) feature, and setting\n `read_only: [\"/etc/resolv.conf\"]`.\n- DNS filter currently works only with frameworks that use `getaddrinfo`/`gethostbyname`\n functions.",
"type": "object",
"properties": {
"enabled": {
"type": [
"boolean",
"null"
]
},
"filter": {
"title": "feature.network.dns.filter {#feature-network-dns-filter}",
"anyOf": [
{
"$ref": "#/$defs/DnsFilterConfig"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
"DnsFilterConfig": {
"description": "List of addresses/ports/subnets that should be resolved through either the remote pod or local\napp, depending how you set this up with either `remote` or `local`.\n\nYou may use this option to specify when DNS resolution is done from the remote pod (which\nis the default behavior when you enable remote DNS), or from the local app (default when\nyou have remote DNS disabled).\n\nTakes a list of values, such as:\n\n- Only queries for hostname `my-service-in-cluster` will go through the remote pod.\n\n```json\n{\n \"remote\": [\"my-service-in-cluster\"]\n}\n```\n\n- Only queries for addresses in subnet `1.1.1.0/24` with service port `1337` will go through the\n remote pod.\n\n```json\n{\n \"remote\": [\"1.1.1.0/24:1337\"]\n}\n```\n\n- Only queries for hostname `google.com` with service port `1337` or `7331` will go through the\n remote pod.\n\n```json\n{\n \"remote\": [\"google.com:1337\", \"google.com:7331\"]\n}\n```\n\n- Only queries for `localhost` with service port `1337` will go through the local app.\n\n```json\n{\n \"local\": [\"localhost:1337\"]\n}\n```\n\n- Only queries with service port `1337` or `7331` will go through the local app.\n\n```json\n{\n \"local\": [\":1337\", \":7331\"]\n}\n```\n\nValid values follow this pattern: `[name|address|subnet/mask][:port]`.\n\nHost names listed in\n[`feature.network.outgoing.filter`](super::outgoing::OutgoingFilterConfig) are automatically\nappended here on the matching side, so DNS resolution stays on the same side as the\nconnection (e.g. `outgoing.filter.local = [\"some.domain.com\"]` implies\n`dns.filter.local = [\"some.domain.com\"]`).",
"oneOf": [
{
"description": "When filters are specified under `remote`, matching DNS queries will go through the remote\npod, everything else will go through local.",
"type": "object",
"properties": {
"remote": {
"$ref": "#/$defs/VecOrSingle"
}
},
"additionalProperties": false,
"required": [
"remote"
]
},
{
"description": "When filters are specified under `local`, matching DNS queries will go through the local\napp , everything else will go through the remote pod.",
"type": "object",
"properties": {
"local": {
"$ref": "#/$defs/VecOrSingle"
}
},
"additionalProperties": false,
"required": [
"local"
]
}
]
},
"DynamodbBranchCopyConfig": {
"description": "Users can choose from the following copy mode to bootstrap their DynamoDB branch database:\n\n- Empty\n\n Creates an empty database. If the source DB connection options are found from the chosen\n target, mirrord operator extracts the database name and create an empty DB. Otherwise, mirrord\n operator looks for the `name` field from the branch DB config object. This option is useful\n for users that run DB migrations themselves before starting the application.\n\n- All\n\n Copies both schema and data of all collections. Supports optional collection filters\n to copy only specific collections or filter documents within collections.",
"oneOf": [
{
"type": "object",
"properties": {
"collections": {
"type": [
"object",
"null"
],
"additionalProperties": {
"$ref": "#/$defs/BranchItemCopyConfig"
}
},
"mode": {
"type": "string",
"const": "empty"
}
},
"additionalProperties": false,
"required": [
"mode"
]
},
{
"type": "object",
"properties": {
"collections": {
"description": "Optional collection filters. If not specified, all collections are copied.\nIf specified, only the listed collections are copied with their optional filters.",
"type": [
"object",
"null"
],
"additionalProperties": {
"$ref": "#/$defs/BranchItemCopyConfig"
}
},
"mode": {
"type": "string",
"const": "all"
}
},
"additionalProperties": false,
"required": [
"mode"
]
}
]
},
"EnvFileConfig": {
"description": "Allows the user to set or override the local process' environment variables with the ones\nfrom the remote pod.\n\nCan be set to one of the options:\n\n1. `false` - Disables the feature, won't have remote environment variables.\n2. `true` - Enables the feature, will obtain remote environment variables.\n3. object - see below (means `true` + additional configuration).\n\nWhich environment variables to load from the remote pod are controlled by setting either\n[`include`](#feature-env-include) or [`exclude`](#feature-env-exclude).\n\nSee the environment variables [reference](https://metalbear.com/mirrord/docs/reference/env/) for more details.\n\n```json\n{\n \"feature\": {\n \"env\": {\n \"include\": \"DATABASE_USER;PUBLIC_ENV;MY_APP_*\",\n \"exclude\": \"DATABASE_PASSWORD;SECRET_ENV\",\n \"override\": {\n \"DATABASE_CONNECTION\": \"db://localhost:7777/my-db\",\n \"LOCAL_BEAR\": \"panda\"\n },\n \"mapping\": {\n \".+_TIMEOUT\": \"1000\"\n }\n }\n }\n}\n```",
"type": "object",
"properties": {
"env_file": {
"title": "feature.env.env_file {#feature-env-env-file}",
"description": "Allows for passing environment variables from an env file.\n\nThese variables will override environment fetched from the remote target.",
"type": [
"string",
"null"
]
},
"exclude": {
"title": "feature.env.exclude {#feature-env-exclude}",
"description": "Include the remote environment variables in the local process that are **NOT** specified by\nthis option.\nVariable names can be matched using `*` and `?` where `?` matches exactly one occurrence of\nany character and `*` matches arbitrary many (including zero) occurrences of any character.\n\nSome of the variables that are excluded by default:\n`PATH`, `HOME`, `HOMEPATH`, `CLASSPATH`, `JAVA_EXE`, `JAVA_HOME`, `PYTHONPATH`.\n\nCan be passed as a list or as a semicolon-delimited string (e.g. `\"VAR;OTHER_VAR\"`).",
"anyOf": [
{
"$ref": "#/$defs/VecOrSingle"
},
{
"type": "null"
}
]
},
"include": {
"title": "feature.env.include {#feature-env-include}",
"description": "Include only these remote environment variables in the local process.\nVariable names can be matched using `*` and `?` where `?` matches exactly one occurrence of\nany character and `*` matches arbitrary many (including zero) occurrences of any character.\n\nCan be passed as a list or as a semicolon-delimited string (e.g. `\"VAR;OTHER_VAR\"`).\n\nSome environment variables are excluded by default (`PATH` for example), including these\nrequires specifying them with `include`",
"anyOf": [
{
"$ref": "#/$defs/VecOrSingle"
},
{
"type": "null"
}
]
},
"load_from_process": {
"title": "feature.env.load_from_process {#feature-env-load_from_process}",
"description": "Allows for changing the way mirrord loads remote environment variables.\nIf set, the variables are fetched after the user application is started.\n\nThis setting is meant to resolve issues when using mirrord via the IntelliJ plugin on WSL\nand the remote environment contains a lot of variables.",
"type": [
"boolean",
"null"
]
},
"mapping": {
"title": "feature.env.mapping {#feature-env-mapping}",
"description": "Specify map of patterns that if matched will replace the value according to specification.\n\n*Capture groups are allowed.*\n\nExample:\n```json\n{\n \".+_TIMEOUT\": \"10000\"\n \"LOG_.+_VERBOSITY\": \"debug\"\n \"(\\w+)_(\\d+)\": \"magic-value\"\n}\n```\n\nWill do the next replacements for environment variables that match:\n\n* `CONNECTION_TIMEOUT: 500` => `CONNECTION_TIMEOUT: 10000`\n\n* `LOG_FILE_VERBOSITY: info` => `LOG_FILE_VERBOSITY: debug`\n\n* `DATA_1234: common-value` => `DATA_1234: magic-value`",
"type": [
"object",
"null"
],
"additionalProperties": {
"type": "string"
}
},
"override": {
"title": "feature.env.override {#feature-env-override}",
"description": "Allows setting or overriding environment variables (locally) with a custom value.\n\nFor example, if the remote pod has an environment variable `REGION=1`, but this is an\nundesirable value, it's possible to use `override` to set `REGION=2` (locally) instead.\n\nEnvironment specified here will also override variables passed via the env file.",
"type": [
"object",
"null"
],
"additionalProperties": {
"type": "string"
}
},
"unset": {
"title": "feature.env.unset {#feature-env-unset}",
"description": "Allows unsetting environment variables in the executed process.\n\nThis is useful for when some system/user-defined environment like `AWS_PROFILE` make the\napplication behave as if it's running locally, instead of using the remote settings.\nThe unsetting happens from extension (if possible)/CLI and when process initializes.\nIn some cases, such as Go the env might not be able to be modified from the process itself.\nThis is case insensitive, meaning if you'd put `AWS_PROFILE` it'd unset both `AWS_PROFILE`\nand `Aws_Profile` and other variations.",
"anyOf": [
{
"$ref": "#/$defs/VecOrSingle"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
"EnvKeyFileConfig": {
"description": "File configuration for the session key.\n\nIn the config file, the key is simply an optional string.\nThe resolution happens in [`MirrordConfig::generate_config`].",
"type": [
"string",
"null"
]
},
"ExperimentalFileConfig": {
"description": "mirrord Experimental features.\nThis shouldn't be used unless someone from MetalBear/mirrord tells you to.",
"type": "object",
"properties": {
"applev": {
"title": "_experimental_ applev {#experimental-applev}",
"description": "Configuration for inspecting and modifying apple variables. macOS only.",
"anyOf": [
{
"$ref": "#/$defs/AppleVariablesConfig"
},
{
"type": "null"
}
]
},
"browser_extension_config": {
"title": "_experimental_ browser_extension_config {#experimental-browser_extension_config}",
"description": "mirrord will open a URL for initiating mirrord browser extension to\nautomatically inject HTTP header that matches the HTTP filter configured in\n`feature.network.incoming.http_filter.header_filter`.",
"type": [
"boolean",
"null"
]
},
"disable_reuseaddr": {
"title": "_experimental_ disable_reuseaddr {#experimental-disable_reuseaddr}",
"description": "Disables the `SO_REUSEADDR` socket option on sockets that mirrord steals/mirrors.\nOn macOS the application can use the same address many times but then we don't steal it\ncorrectly. This probably should be on by default but we want to gradually roll it out.\n<https://github.com/metalbear-co/mirrord/issues/2819>\nThis option applies only on macOS.\n\nDefaults to `true` in OSS.\nDefaults to `false` in mfT.",
"type": [
"boolean",
"null"
]
},
"dlopen_cgo": {
"title": "_experimental_ dlopen_cgo {#experimental-dlopen_cgo}",
"description": "Useful when the user's application loads a c-shared golang library dynamically.\n\nDefaults to `false`.",
"type": [
"boolean",
"null"
]
},
"enable_exec_hooks_linux": {
"title": "_experimental_ enable_exec_hooks_linux {#experimental-enable_exec_hooks_linux}",
"description": "Enables exec hooks on Linux. Enable Linux hooks can fix issues when the application\nshares sockets with child commands (e.g Python web servers with reload),\nbut the feature is not stable and may cause other issues.",
"type": [
"boolean",
"null"
]
},
"go_asmcgocall": {
"title": "_experimental_ go_asmcgocall {#go_asmcgocall}",
"description": "On x86-64, route the Go 1.25+ syscall hook through the Go runtime's own\n`runtime.asmcgocall` for the switch to and from the `g0` system stack, instead of the\nhand-rolled assembly switch. This mirrors what the arm64 hook already does and avoids\ncorrupting the scheduler state (`g.sched`) of goroutines blocked in a syscall, which\ncan crash cgo-heavy Go programs. No effect on arm64, which always uses\n`runtime.asmcgocall`.\n\nDefaults to `true` in OSS.\nDefaults to `false` in mfT.",
"type": [
"boolean",
"null"
]
},
"guard_std_fds": {
"title": "_experimental_ guard_std_fds {#experimental-guard_std_fds}",
"description": "Ensures the standard fds (0-2) are open when the layer initializes, pointing any closed\none at `/dev/null`. Protects the layer's internal fds (most importantly its connection to\nthe internal proxy) from being assigned a std fd number and reconfigured by the\napplication's runtime, e.g. `libuv` setting `O_NONBLOCK` on what it considers stdin.\n<https://github.com/metalbear-co/mirrord/issues/4622>\n\nDEPRECATED, WILL BE REMOVED",
"type": [
"boolean",
"null"
],
"deprecated": true
},
"hide_ipv6_interfaces": {
"title": "_experimental_ hide_ipv6_interfaces {#experimental-hide_ipv6_interfaces}",
"description": "Enables `getifaddrs` hook that removes IPv6 interfaces from the list returned by libc.",
"type": [
"boolean",
"null"
]
},
"idle_local_http_connection_timeout": {
"title": "_experimental_ idle_local_http_connection_timeout {#experimental-idle_local_http_connection_timeout}",
"description": "Sets a timeout for idle local HTTP connections (in milliseconds).\n\nHTTP requests stolen with a filter are delivered to the local application\nfrom a HTTP connection made from the local machine. Once a request is delivered,\nthe connection is cached for some time, so that it can be reused to deliver\nthe next request.\n\nThis timeout determines for how long such connections are cached.\n\nSet to 0 to disable caching local HTTP connections (connections will be dropped as soon as\nthe request is delivered).\n\nDefaults to 3000ms.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
},
"ignore_system_proxy_config": {
"title": "_experimental_ ignore_system_proxy_config {#experimental-ignore_system_proxy_config}",
"description": "Disables any system wide proxy configuration for affecting the running application.",
"type": [
"boolean",
"null"
]
},
"latency": {
"title": "_experimental_ latency {#experimental-latency}",
"description": "Configuration for adding artificial latency to outgoing network operations.\n\nDEPRECATED, WILL BE REMOVED\nPlease use the mirrord chaos feature instead.",
"anyOf": [
{
"$ref": "#/$defs/LatencyFileConfig"
},
{
"type": "null"
}
],
"deprecated": true
},
"non_blocking_tcp_connect": {
"title": "_experimental_non_blocking_tcp_connect {#experimental-non_blocking_tcp_connect}",
"description": "Enables better support for outgoing connections using\nnon-blocking TCP sockets.\n\nDue to technical reasons, enabling this causes the existence\nof the internal proxy acting as a TCP proxy observable to the\napplication. Notable consequences of this include:\n\n1. `getsockname(2)` will always return a localhost address,\n2. `connect(2)` will succeed immediately, before the remote side of the connection is\n established. If the remote side of the connection fails, the connection to the app will\n be closed after the fact.\n\nEssentially your application has to assume that it might be\nsitting behind a TCP proxy. Most \"standard\" apps (e.g. HTTP\nclients) should be unaffected, but custom implementations of\nother protocols may not.\n\nDEPRECATED, WILL BE REMOVED",
"type": [
"boolean",
"null"
],
"deprecated": true
},
"sip_log_destination": {
"title": "_experimental_ sip_log_destination {#experimental-sip_log_destination}",
"description": "Writes basic fork-safe SIP patching logs to a destination file.\nUseful for seeing the state of SIP when `stdout` may be affected by another process.",
"type": [
"string",
"null"
]
},
"sip_utils": {
"title": "_experimental_ sip_utils {#experimental-sip_utils}",
"description": "Extract pre-built SIP utility binaries into `~/.mirrord/binaries` on macOS and uses\nthem in place of SIP-patching the originals.\nThis shouldn't be used unless someone from MetalBear/mirrord tells you to.\n\nDEPRECATED, WILL BE REMOVED",
"type": [
"boolean",
"null"
],
"deprecated": true
},
"tcp_ping4_mock": {
"title": "_experimental_ tcp_ping4_mock {#experimental-tcp_ping4_mock}",
"description": "<https://github.com/metalbear-co/mirrord/issues/2421#issuecomment-2093200904>",
"type": [
"boolean",
"null"
]
},
"trust_any_certificate": {
"title": "_experimental_ trust_any_certificate {#experimental-trust_any_certificate}",
"description": "Enables trusting any certificate on macOS, useful for <https://github.com/golang/go/issues/51991#issuecomment-2059588252>",
"type": [
"boolean",
"null"
]
},
"use_dev_null": {
"title": "_experimental_ use_dev_null {#experimental-use_dev_null}",
"description": "Uses /dev/null for creating local fake files (should be better than using /tmp)",
"type": [
"boolean",
"null"
]
}
},
"additionalProperties": false
},
"ExternalProxyFileConfig": {
"description": "Configuration for the external proxy mirrord spawns when using the `mirrord container` command.\nThis proxy is used to allow the internal proxy running in sidecar to connect to the mirrord\nagent.\n\nIf you get `ConnectionRefused` errors, increasing the timeouts a bit might solve the issue.\n\n```json\n{\n \"external_proxy\": {\n \"start_idle_timeout\": 30,\n \"idle_timeout\": 5\n }\n}\n```",
"type": "object",
"properties": {
"host_ip": {
"title": "external_proxy.host_ip {#external_proxy-host_ip}",
"description": "Specify a custom host ip addr to listen on.\n\nThis address must be accessible from within the container.\nIf not specified, mirrord will try and resolve a local address to use.\n\n- If you're running inside WSL, and encountering problems, try setting this to `0.0.0.0`,\n and `container.override_host_ip` to the internal container runtime address (for docker,\n this would be what `host.docker.internal` resolved to, which by default is\n `192.168.65.254`).",
"type": [
"string",
"null"
],
"format": "ip"
},
"idle_timeout": {
"title": "external_proxy.idle_timeout {#external_proxy-idle_timeout}",
"description": "How much time to wait while we don't have any active connections before exiting.\n\nCommon cases would be running a chain of processes that skip using the layer\nand don't connect to the proxy.\n\n```json\n{\n \"external_proxy\": {\n \"idle_timeout\": 30\n }\n}\n```",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
},
"json_log": {
"title": "external_proxy.json_log {#external_proxy-json_log}",
"description": "Whether the proxy should output logs in JSON format. If false, logs are output in\nhuman-readable format.\n\nDefaults to true.",
"type": [
"boolean",
"null"
]
},
"log_destination": {
"title": "external_proxy.log_destination {#external_proxy-log_destination}",
"description": "Set the log destination for the external proxy.\n\n1. If the provided path ends with a separator (`/` on UNIX, `\\` on Windows), it will be\n treated as a path to directory where the log file should be created.\n2. Otherwise, if the path exists, mirrord will check if it's a directory or not.\n3. Otherwise, it will be treated as a path to the log file.\n\nmirrord will auto create all parent directories.\n\nDefaults to a randomized path inside the temporary directory.",
"type": [
"string",
"null"
]
},
"log_level": {
"title": "external_proxy.log_level {#external_proxy-log_level}",
"description": "Set the log level for the external proxy.\n\nThe value should follow the RUST_LOG convention (i.e `mirrord=trace`).\n\nDefaults to `mirrord=info,warn`.",
"type": [
"string",
"null"
]
},
"start_idle_timeout": {
"title": "external_proxy.start_idle_timeout {#external_proxy-start_idle_timeout}",
"description": "How much time to wait for the first connection to the external proxy in seconds.\n\nCommon cases would be running with dlv or any other debugger, which sets a breakpoint\non process execution, delaying the layer startup and connection to the external proxy.\n\n```json\n{\n \"external_proxy\": {\n \"start_idle_timeout\": 60\n }\n}\n```",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
},
"tls_enable": {
"description": "<!--${internal}-->\n\nWhether to use TLS or a plain TCP when accepting a connection from the internal proxy\nsidecar.",
"type": [
"boolean",
"null"
]
}
},
"additionalProperties": false
},
"FeatureFileConfig": {
"description": "Controls mirrord features.\n\nSee the\n[technical reference, Technical Reference](https://metalbear.com/mirrord/docs/reference/)\nto learn more about what each feature does.\n\nThe [`env`](#feature-env), [`fs`](#feature-fs) and [`network`](#feature-network) options\nhave support for a shortened version, that you can see [here](#root-shortened).\n\n```json\n{\n \"feature\": {\n \"env\": {\n \"include\": \"DATABASE_USER;PUBLIC_ENV\",\n \"exclude\": \"DATABASE_PASSWORD;SECRET_ENV\",\n \"override\": {\n \"DATABASE_CONNECTION\": \"db://localhost:7777/my-db\",\n \"LOCAL_BEAR\": \"panda\"\n }\n },\n \"fs\": {\n \"mode\": \"write\",\n \"read_write\": \".+\\\\.json\" ,\n \"read_only\": [ \".+\\\\.yaml\", \".+important-file\\\\.txt\" ],\n \"local\": [ \".+\\\\.js\", \".+\\\\.mjs\" ],\n \"prefetch\": [ \"/etc/ssl\" ]\n },\n \"network\": {\n \"incoming\": {\n \"mode\": \"steal\",\n \"http_filter\": {\n \"header_filter\": \"^baggage: .*mirrord-session={{ key }}.*$\"\n },\n \"port_mapping\": [[ 7777, 8888 ]],\n \"ignore_localhost\": false,\n \"ignore_ports\": [9999, 10000]\n },\n \"outgoing\": {\n \"tcp\": true,\n \"udp\": true,\n \"filter\": {\n \"local\": [\"tcp://1.1.1.0/24:1337\", \"1.1.5.0/24\", \"google.com\", \":53\"]\n },\n \"ignore_localhost\": false,\n \"unix_streams\": \"bear.+\"\n },\n \"dns\": false\n },\n \"copy_target\": false,\n \"hostname\": true\n }\n}\n```",
"type": "object",
"properties": {
"copy_target": {
"title": "feature.copy_target {#feature-copy_target}",
"description": "Creates a new copy of the target. mirrord will use this copy instead of the original target\n(e.g. intercept network traffic). This feature requires a [mirrord operator](https://metalbear.com/mirrord/docs/overview/teams/?utm_source=copytarget).\n\nThis feature is not compatible with rollout targets and running without a target\n(`targetless` mode).",
"anyOf": [
{
"$ref": "#/$defs/CopyTargetFileConfig"
},
{
"type": "null"
}
]
},
"db_branches": {
"title": "feature.db_branches {#feature-db_branches}",
"description": "Configuration for the database branching feature.",
"anyOf": [
{
"$ref": "#/$defs/DatabaseBranchesConfig"
},
{
"type": "null"
}
]
},
"env": {
"title": "feature.env {#feature-env}",
"anyOf": [
{
"$ref": "#/$defs/ToggleableConfig"
},
{
"type": "null"
}
]
},
"fs": {
"title": "feature.fs {#feature-fs}",
"anyOf": [
{
"$ref": "#/$defs/ToggleableConfig2"
},
{
"type": "null"
}
]
},
"hostname": {
"title": "feature.hostname {#feature-hostname}",
"description": "Should mirrord return the hostname of the target pod when calling `gethostname`",
"type": [
"boolean",
"null"
]
},
"magic": {
"title": "feature.magic {#feature-magic}",
"description": "Sensible defaults that improve the experience for most users. Each flag can be disabled\nindividually if it conflicts with your setup.",
"anyOf": [
{
"$ref": "#/$defs/MagicFileConfig"
},
{
"type": "null"
}
]
},
"network": {
"title": "feature.network {#feature-network}",
"anyOf": [
{
"$ref": "#/$defs/ToggleableConfig3"
},
{
"type": "null"
}
]
},
"preview": {
"title": "feature.preview {#feature-preview}",
"description": "Configuration for preview environments.",
"anyOf": [
{
"$ref": "#/$defs/PreviewFileConfig"
},
{
"type": "null"
}
]
},
"split_queues": {
"title": "feature.split_queues {#feature-split_queues}",
"description": "Define filters to split queues by, and make your local application consume only messages\nthat match those filters.\nIf you don't specify any filter for a queue that is however declared in the\n`MirrordWorkloadQueueRegistry` of the target you're using, a match-nothing filter\nwill be used, and your local application will not receive any messages from that queue.",
"anyOf": [
{
"$ref": "#/$defs/SplitQueuesConfig"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
"FileAgentDnsConfig": {
"description": "Configuration options for how the agent performs DNS resolution.",
"type": "object",
"properties": {
"attempts": {
"title": "agent.dns.attempts {#agent-dns-attempts}",
"description": "Specifies the number of DNS resolution attempts the agent will make before failing.\nSetting this too high may cause the internal proxy to time out and exit.",
"type": [
"integer",
"null"
],
"format": "uint32",
"minimum": 0
},
"timeout": {
"title": "agent.dns.timeout {#agent-dns-timeout}",
"description": "Specifies how long (in seconds) the agent will wait for a DNS response before timing out.\nIf not specified the agent uses a default value of 1 second.\nSetting this too high may cause the internal proxy to time out and exit.",
"type": [
"integer",
"null"
],
"format": "uint32",
"minimum": 0
}
},
"additionalProperties": false
},
"FsModeConfig": {
"title": "feature.fs.mode {#feature-fs-mode}",
"description": "Configuration for enabling read-only or read-write file operations.\n\nThese options are overridden by user specified overrides and mirrord default overrides.\n\nIf you set [`\"localwithoverrides\"`](#feature-fs-mode-localwithoverrides) then some files\ncan be read/write remotely based on our default/user specified.\nDefault option for general file configuration.\n\nThe accepted values are: `\"local\"`, `\"localwithoverrides\"`, `\"read\"`, or `\"write\"`.",
"oneOf": [
{
"title": "feature.fs.mode.local {#feature-fs-mode-local}",
"description": "mirrord won't do anything fs-related, all operations will be local.",
"type": "string",
"const": "local"
},
{
"title": "feature.fs.mode.localwithoverrides {#feature-fs-mode-localwithoverrides}",
"description": "mirrord will run overrides on some file operations, but most will be local.",
"type": "string",
"const": "localwithoverrides"
},
{
"title": "feature.fs.mode.read {#feature-fs-mode-read}",
"description": "mirrord will read files from the remote, but won't write to them.",
"type": "string",
"const": "read"
},
{
"title": "feature.fs.mode.write {#feature-fs-mode-write}",
"description": "mirrord will read/write from the remote.",
"type": "string",
"const": "write"
}
]
},
"FsUserConfig": {
"title": "feature.fs {#fs}",
"description": "Changes file operations behavior based on user configuration.\n\nSee the file operations [reference](https://metalbear.com/mirrord/docs/reference/fileops/)\nfor more details, and [fs advanced](#fs-advanced) for more information on how to fully setup\nmirrord file operations.\n\n### Minimal `fs` config {#fs-minimal}\n\n```json\n{\n \"feature\": {\n \"fs\": \"read\"\n }\n}\n```\n\n### Advanced `fs` config {#fs-advanced}\n\n```json\n{\n \"feature\": {\n \"fs\": {\n \"mode\": \"write\",\n \"read_write\": \".+\\\\.json\" ,\n \"read_only\": [ \".+\\\\.yaml\", \".+important-file\\\\.txt\" ],\n \"local\": [ \".+\\\\.js\", \".+\\\\.mjs\" ],\n \"prefetch\": [ \"/etc/ssl\" ]\n }\n }\n}\n```",
"anyOf": [
{
"description": "<!--${internal}-->\nBasic configuration that controls the env vars `MIRRORD_FILE_OPS` and `MIRRORD_FILE_RO_OPS`\n(default).",
"$ref": "#/$defs/FsModeConfig"
},
{
"description": "<!--${internal}-->\nAllows the user to specify both [`FsModeConfig`] (as above), and configuration for the\noverrides.",
"$ref": "#/$defs/AdvancedFsUserConfig"
}
]
},
"GenericCopyConfig": {
"description": "<!--${internal}-->\nCopy Job for a generic branch. Documented on [`GenericBranchConfig`].",
"type": "object",
"properties": {
"args": {
"description": "Entrypoint args override for the copy Job container.",
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
},
"command": {
"description": "Entrypoint command override for the copy Job container.",
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
},
"image": {
"description": "Full image reference for the copy Job container, including the tag.",
"type": "string"
}
},
"additionalProperties": false,
"required": [
"image"
]
},
"GenericReadinessConfig": {
"description": "<!--${internal}-->\nReadiness check for a generic branch container. Documented on [`GenericBranchConfig`].",
"oneOf": [
{
"description": "TCP probe on the branch `port` (the default when `readiness` is not set).",
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "tcp"
}
},
"additionalProperties": false,
"required": [
"type"
]
},
{
"description": "Run a command inside the branch container; ready when it exits 0.",
"type": "object",
"properties": {
"command": {
"type": "array",
"items": {
"type": "string"
}
},
"type": {
"type": "string",
"const": "exec"
}
},
"additionalProperties": false,
"required": [
"type",
"command"
]
},
{
"description": "HTTP GET probe; ready on a 2xx/3xx response. `port` defaults to the branch `port`.",
"type": "object",
"properties": {
"path": {
"type": "string"
},
"port": {
"type": [
"integer",
"null"
],
"format": "uint16",
"maximum": 65535,
"minimum": 0
},
"type": {
"type": "string",
"const": "http_get"
}
},
"additionalProperties": false,
"required": [
"type",
"path"
]
}
]
},
"HttpFilterFileConfig": {
"description": "Filter configuration for the HTTP traffic stealer feature.\n\nAllows the user to set a filter (regex) for the HTTP headers, so that the stealer traffic\nfeature only captures HTTP requests that match the specified filter, forwarding unmatched\nrequests to their original destinations.\n\nOnly does something when [`feature.network.incoming.mode`](#feature-network-incoming-mode) is\nset as `\"steal\"`, ignored otherwise.\n\nThe recommended way to filter a single developer session is to propagate a W3C `baggage` or\n`tracestate` entry such as `mirrord-session={{ key }}` from the caller, and match that value\nhere. This works well across proxies, service meshes, and tracing-aware clients.\n\nFor example, to filter on a `baggage` header:\n```json\n{\n \"header_filter\": \"^baggage: .*mirrord-session={{ key }}.*$\"\n}\n```\nSetting that filter will make mirrord only steal requests whose `baggage` header contains\n`mirrord-session={{ key }}`.\n\nIf your traffic already propagates `tracestate`, you can filter on it the same way:\n```json\n{\n \"header_filter\": \"^tracestate: .*mirrord-session={{ key }}.*$\"\n}\n```\n\nFor example, to filter based on path:\n```json\n{\n \"path_filter\": \"^/api/\"\n}\n```\nSetting this filter will make mirrord only steal requests to URIs starting with \"/api/\".\n\n\nThis can be useful for filtering out Kubernetes liveness, readiness and startup probes.\nFor example, for avoiding stealing any probe sent by kubernetes, you can set this filter:\n```json\n{\n \"header_filter\": \"^User-Agent: (?!kube-probe)\"\n}\n```\nSetting this filter will make mirrord only steal requests that **do** have a user agent that\n**does not** begin with \"kube-probe\".\n\nSimilarly, you can exclude certain paths using a negative look-ahead:\n```json\n{\n \"path_filter\": \"^(?!/health/)\"\n}\n```\nSetting this filter will make mirrord only steal requests to URIs that do not start with\n\"/health/\".\n\nWith `all_of` and `any_of`, you can use multiple HTTP filters at the same time.\n\nIf you want to steal HTTP requests that match **every** pattern specified, use `all_of`.\nFor example, this filter steals only `POST` requests to endpoint `/api/my-endpoint` whose\n`baggage` header contains `mirrord-session={{ key }}`.\n```json\n{\n \"all_of\": [\n { \"header\": \"^baggage: .*mirrord-session={{ key }}.*$\" },\n { \"path\": \"^/api/my-endpoint$\" },\n { \"method\": \"POST\" }\n ]\n}\n```\n\nIf you want to steal HTTP requests that match **any** of the patterns specified, use `any_of`.\nFor example, this filter steals HTTP requests to `/api/my-endpoint`, or requests whose\n`baggage` header contains `mirrord-session={{ key }}`.\n```json\n{\n \"any_of\": [\n { \"header\": \"^baggage: .*mirrord-session={{ key }}.*$\" },\n { \"path\": \"^/api/my-endpoint$\" }\n ]\n}\n```",
"type": "object",
"properties": {
"all_of": {
"title": "feature.network.incoming.http_filter.all_of {#feature-network-incoming-http_filter-all_of}",
"description": "An array of HTTP filters.\n\nEach inner filter specifies a header, path, method, body, or jq filter.\nRequests must match all of the filters to be stolen.\n\nCannot be an empty list.\n\nExample:\n```json\n{\n \"all_of\": [\n { \"header\": \"^baggage: .*mirrord-session={{ key }}.*$\" },\n { \"path\": \"^/api/v1/my-endpoint$\" },\n { \"method\": \"POST\" }\n ]\n}\n```",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/$defs/InnerFilter"
}
},
"any_of": {
"title": "feature.network.incoming.http_filter.any_of {#feature-network-incoming-http_filter-any_of}",
"description": "An array of HTTP filters.\n\nEach inner filter specifies a header, path, method, body, or jq filter.\nRequests must match at least one of the filters to be stolen.\n\nCannot be an empty list.\n\nExample:\n```json\n{\n \"any_of\": [\n { \"header\": \"^baggage: .*mirrord-session={{ key }}.*$\" },\n { \"header\": \"^tracestate: .*mirrord-session={{ key }}.*$\" },\n { \"path\": \"^/api/v1/my-endpoint$\" }\n ]\n}\n```",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/$defs/InnerFilter"
}
},
"body_filter": {
"title": "feature.network.incoming.http_filter.body_filter {#feature-network-incoming-http-body-filter}",
"description": "Matches the request based on the contents of its body.",
"anyOf": [
{
"$ref": "#/$defs/BodyFilter"
},
{
"type": "null"
}
]
},
"header_filter": {
"title": "feature.network.incoming.http_filter.header_filter {#feature-network-incoming-http-header-filter}",
"description": "Supports regexes validated by the\n[`fancy-regex`](https://docs.rs/fancy-regex/latest/fancy_regex/) crate.\n\nThe HTTP traffic feature converts the HTTP headers to `HeaderKey: HeaderValue`,\ncase-insensitive.\n\nThe recommended pattern is to match a W3C `baggage` or `tracestate` entry such as\n`mirrord-session={{ key }}`.",
"type": [
"string",
"null"
]
},
"header_filter_jq": {
"title": "feature.network.incoming.http_filter.header_filter_jq {#feature-network-incoming-http-header-filter-jq}",
"description": "Supports jq expressions, matches when the expression returns\n`true`. The expression is evaluated on each present header in\nthe request, in `HeaderKey: HeaderValue` format.",
"type": [
"string",
"null"
]
},
"method_filter": {
"title": "feature.network.incoming.http_filter.method_filter {#feature-network-incoming-http-method-filter}",
"description": "Supports standard [HTTP methods](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Methods), and non-standard HTTP methods.\n\nCase-insensitive. If the request method matches the filter, the request is stolen.",
"type": [
"string",
"null"
]
},
"path_filter": {
"title": "feature.network.incoming.http_filter.path_filter {#feature-network-incoming-http-path-filter}",
"description": "Supports regexes validated by the\n[`fancy-regex`](https://docs.rs/fancy-regex/latest/fancy_regex/) crate.\n\nCase-insensitive. Tries to find match in the path (without query) and path+query.\nIf any of the two matches, the request is stolen.",
"type": [
"string",
"null"
]
},
"ports": {
"title": "feature.network.incoming.http_filter.ports {#feature-network-incoming-http_filter-ports}",
"description": "Activate the HTTP traffic filter only for these ports. When\nabsent, filtering will be done for all ports.",
"anyOf": [
{
"$ref": "#/$defs/VecOrSingle2"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
"IamAuthConfig": {
"description": "IAM authentication for the source database.\nUse this when your source database (AWS RDS, GCP Cloud SQL) requires IAM authentication\ninstead of password-based authentication.\n\nEnvironment variable sources follow the same pattern as `connection.url`:\n- `{ \"type\": \"env\", \"variable\": \"VAR_NAME\" }` - direct env var from pod spec\n- `{ \"type\": \"env_from\", \"variable\": \"VAR_NAME\" }` - from configMapRef/secretRef",
"oneOf": [
{
"description": "For AWS RDS/Aurora IAM authentication, set `type` to `\"aws_rds\"`.\n\nCredentials for signing the RDS auth token come from one of two setups:\n- Static keys: the operator copies `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, and\n optionally `AWS_SESSION_TOKEN` from the target pod's environment (or from the env vars\n named in the fields below) to the branch pod.\n- IRSA / EKS Pod Identity: when the target pod has no static keys, the branch pod runs under\n the target's service account and receives the same IAM role. No key fields are needed; `{\n \"type\": \"aws_rds\" }` is enough.\n\nExample with explicit env var sources:\n```json\n{\n \"iam_auth\": {\n \"type\": \"aws_rds\",\n \"region\": { \"type\": \"env\", \"variable\": \"MY_AWS_REGION\" },\n \"access_key_id\": { \"type\": \"env_from\", \"variable\": \"AWS_KEY\" }\n }\n}\n```\n\nParameters:\n- `region`: AWS region. If not specified, uses AWS_REGION or AWS_DEFAULT_REGION from the\n target pod. With IRSA, set it explicitly if neither var is in the target's pod spec.\n- `access_key_id`: AWS Access Key ID. If not specified, uses AWS_ACCESS_KEY_ID.\n- `secret_access_key`: AWS Secret Access Key. If not specified, uses AWS_SECRET_ACCESS_KEY.\n- `session_token`: AWS Session Token (for temporary credentials). If not specified, uses\n AWS_SESSION_TOKEN.",
"type": "object",
"properties": {
"access_key_id": {
"anyOf": [
{
"$ref": "#/$defs/DbBranchingConnectionSourceKind"
},
{
"type": "null"
}
]
},
"region": {
"anyOf": [
{
"$ref": "#/$defs/DbBranchingConnectionSourceKind"
},
{
"type": "null"
}
]
},
"secret_access_key": {
"anyOf": [
{
"$ref": "#/$defs/DbBranchingConnectionSourceKind"
},
{
"type": "null"
}
]
},
"session_token": {
"anyOf": [
{
"$ref": "#/$defs/DbBranchingConnectionSourceKind"
},
{
"type": "null"
}
]
},
"type": {
"type": "string",
"const": "aws_rds"
}
},
"additionalProperties": false,
"required": [
"type"
]
},
{
"description": "For GCP Cloud SQL IAM authentication, set `type` to `\"gcp_cloud_sql\"`.\n\nExample for GCP Cloud SQL with credentials from a secret:\n```json\n{\n \"iam_auth\": {\n \"type\": \"gcp_cloud_sql\",\n \"credentials_json\": { \"type\": \"env_from\", \"variable\": \"GOOGLE_APPLICATION_CREDENTIALS_JSON\" }\n }\n}\n```\n\nThe init container must have GCP credentials (via Workload Identity or service account key).\nUse either `credentials_json` OR `credentials_path`, not both.\n\nParameters:\n- `credentials_json`: Inline service account JSON key content. Specify the env var that\n contains the raw JSON content of the service account key. Example: ` { \"type\": \"env\",\n \"variable\": \"GOOGLE_APPLICATION_CREDENTIALS_JSON\" } `.\n- `credentials_path`: Path to service account JSON key file. Specify the env var that\n contains the file path to the service account key. The file must be accessible from the\n init container. Example: `{\"type\": \"env\", \"variable\": \"GOOGLE_APPLICATION_CREDENTIALS\"}`.\n- `project`: GCP project ID. If not specified, uses GOOGLE_CLOUD_PROJECT or GCP_PROJECT.",
"type": "object",
"properties": {
"credentials_json": {
"anyOf": [
{
"$ref": "#/$defs/DbBranchingConnectionSourceKind"
},
{
"type": "null"
}
]
},
"credentials_path": {
"anyOf": [
{
"$ref": "#/$defs/DbBranchingConnectionSourceKind"
},
{
"type": "null"
}
]
},
"project": {
"anyOf": [
{
"$ref": "#/$defs/DbBranchingConnectionSourceKind"
},
{
"type": "null"
}
]
},
"type": {
"type": "string",
"const": "gcp_cloud_sql"
}
},
"additionalProperties": false,
"required": [
"type"
]
}
]
},
"IncomingAdvancedFileConfig": {
"title": "incoming (advanced setup)",
"description": "Advanced user configuration for network incoming traffic.",
"type": "object",
"properties": {
"http_filter": {
"title": "HTTP Filter",
"description": "Sets up the HTTP traffic filter (currently, only useful when `incoming: steal`).\n\nSee [`filter`](##filter) for details.",
"anyOf": [
{
"$ref": "#/$defs/ToggleableConfig5"
},
{
"type": "null"
}
]
},
"https_delivery": {
"title": "https_delivery",
"description": "DEPRECATED: use `tls_delivery` instead.",
"anyOf": [
{
"$ref": "#/$defs/LocalTlsDelivery"
},
{
"type": "null"
}
]
},
"ignore_localhost": {
"title": "ignore_localhost",
"description": "Consider removing when adding <https://github.com/metalbear-co/mirrord/issues/702>",
"type": [
"boolean",
"null"
]
},
"ignore_ports": {
"title": "ignore_ports",
"description": "Ports to ignore when mirroring/stealing traffic. Useful if you want specific ports to be\nused locally only.\n\nMutually exclusive with [`ports`](###ports).",
"type": [
"array",
"null"
],
"items": {
"type": "integer",
"format": "uint16",
"maximum": 65535,
"minimum": 0
}
},
"listen_ports": {
"title": "listen_ports",
"description": "Mapping for local ports to actually used local ports.\nWhen application listens on a port while steal/mirror is active\nwe fallback to random ports to avoid port conflicts.\nUsing this configuration will always use the specified port.\nIf this configuration doesn't exist, mirrord will try to listen on the original port\nand if it fails it will assign a random port\n\nThis is useful when you want to access ports exposed by your service locally\nFor example, if you have a service that listens on port `80` and you want to access it,\nyou probably can't listen on `80` without sudo, so you can use `[[80, 4480]]`\nthen access it on `4480` while getting traffic from remote `80`.\nThe value of `port_mapping` doesn't affect this.",
"type": [
"array",
"null"
],
"items": {
"type": "array",
"maxItems": 2,
"minItems": 2,
"prefixItems": [
{
"type": "integer",
"format": "uint16",
"maximum": 65535,
"minimum": 0
},
{
"type": "integer",
"format": "uint16",
"maximum": 65535,
"minimum": 0
}
]
}
},
"mode": {
"title": "mode",
"description": "Allows selecting between mirroring or stealing traffic.\n\nSee [`mode`](##mode (incoming)) for details.",
"anyOf": [
{
"$ref": "#/$defs/IncomingMode"
},
{
"type": "null"
}
]
},
"on_concurrent_steal": {
"title": "on_concurrent_steal",
"description": "(Operator Only): if value of override will force close any other connections on requested\ntarget",
"anyOf": [
{
"$ref": "#/$defs/ConcurrentSteal"
},
{
"type": "null"
}
]
},
"port_mapping": {
"title": "port_mapping",
"description": "Mapping for local ports to remote ports.\n\nThis is useful when you want to mirror/steal a port to a different port on the remote\nmachine. For example, your local process listens on port `9333` and the container listens\non port `80`. You'd use `[[9333, 80]]`",
"type": [
"array",
"null"
],
"items": {
"type": "array",
"maxItems": 2,
"minItems": 2,
"prefixItems": [
{
"type": "integer",
"format": "uint16",
"maximum": 65535,
"minimum": 0
},
{
"type": "integer",
"format": "uint16",
"maximum": 65535,
"minimum": 0
}
]
}
},
"ports": {
"title": "ports",
"description": "List of ports to mirror/steal traffic from. Other ports will remain local.\n\nMutually exclusive with [`ignore_ports`](###ignore_ports).",
"type": [
"array",
"null"
],
"items": {
"type": "integer",
"format": "uint16",
"maximum": 65535,
"minimum": 0
}
},
"tls_delivery": {
"title": "tls_delivery",
"description": "(Operator Only): configures how mirrord delivers stolen TLS traffic\nto the local application.",
"anyOf": [
{
"$ref": "#/$defs/LocalTlsDelivery"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
"IncomingFileConfig": {
"title": "incoming (network)",
"description": "Controls the incoming TCP traffic feature.\n\nSee the incoming [reference](https://metalbear.com/mirrord/docs/reference/traffic/#incoming) for more\ndetails.\n\nIncoming traffic supports 2 modes of operation:\n\n1. Mirror (**default**): Sniffs the TCP data from a port, and forwards a copy to the interested\n listeners;\n\n2. Steal: Captures the TCP data from a port, and forwards it to the local process, see\n [`steal`](##steal);\n\n### Minimal `incoming` config\n\n```json\n{\n \"feature\": {\n \"network\": {\n \"incoming\": \"steal\"\n }\n }\n}\n```\n\n### Advanced `incoming` config\n\n```json\n{\n \"feature\": {\n \"network\": {\n \"incoming\": {\n \"mode\": \"steal\",\n \"http_filter\": {\n \"header_filter\": \"^baggage: .*mirrord-session={{ key }}.*$\"\n },\n \"port_mapping\": [[ 7777, 8888 ]],\n \"ignore_localhost\": false,\n \"ignore_ports\": [9999, 10000],\n \"listen_ports\": [[80, 8111]]\n }\n }\n }\n}\n```",
"anyOf": [
{
"anyOf": [
{
"$ref": "#/$defs/IncomingMode"
},
{
"type": "null"
}
]
},
{
"$ref": "#/$defs/IncomingAdvancedFileConfig"
}
]
},
"IncomingMode": {
"description": "Allows selecting between mirroring or stealing traffic.\n\nCan be set to either `\"mirror\"` (default), `\"steal\"` or `\"off\"`.\n\n- `\"mirror\"`: Sniffs on TCP port, and send a copy of the data to listeners.\n- `\"off\"`: Disables the incoming network feature.\n- `\"steal\"`: Supports 2 modes of operation:\n\n1. Port traffic stealing: Steals all TCP data from a port, which is selected whenever the user\n listens in a TCP socket (enabling the feature is enough to make this work, no additional\n configuration is needed);\n\n2. HTTP traffic stealing: Steals only HTTP traffic, mirrord tries to detect if the incoming data\n on a port is HTTP (in a best-effort kind of way, not guaranteed to be HTTP), and steals the\n traffic on the port if it is HTTP;",
"oneOf": [
{
"description": "<!--${internal}-->\n### mirror\n\nSniffs on TCP port, and send a copy of the data to listeners.",
"type": "string",
"const": "mirror"
},
{
"description": "<!--${internal}-->\n### steal\n\nStealer supports 2 modes of operation:\n\n1. Port traffic stealing: Steals all TCP data from a port, which is selected whenever the\n user listens in a TCP socket (enabling the feature is enough to make this work, no\n additional configuration is needed);\n\n2. HTTP traffic stealing: Steals only HTTP traffic, mirrord tries to detect if the incoming\n data on a port is HTTP (in a best-effort kind of way, not guaranteed to be HTTP), and\n steals the traffic on the port if it is HTTP;",
"type": "string",
"const": "steal"
},
{
"description": "<!--${internal}-->\n### Off\n\nDisables the incoming network feature.",
"type": "string",
"const": "off"
}
]
},
"InnerFilter": {
"anyOf": [
{
"title": "feature.network.incoming.inner_filter.header_filter {#feature-network-incoming-inner-header-filter}",
"description": "Supports regexes validated by the\n[`fancy-regex`](https://docs.rs/fancy-regex/latest/fancy_regex/) crate.\n\nThe HTTP traffic feature converts the HTTP headers to `HeaderKey: HeaderValue`,\ncase-insensitive.\n\nPrefer matching W3C `baggage` or `tracestate` entries such as\n`mirrord-session={{ key }}` when you want to isolate one developer session.",
"type": "object",
"properties": {
"header": {
"type": "string"
}
},
"additionalProperties": false,
"required": [
"header"
]
},
{
"title": "feature.network.incoming.inner_filter.path_filter {#feature-network-incoming-inner-path-filter}",
"description": "Supports regexes validated by the\n[`fancy-regex`](https://docs.rs/fancy-regex/latest/fancy_regex/) crate.\n\nCase-insensitive. Tries to find match in the path (without query) and path+query.\nIf any of the two matches, the request is stolen.",
"type": "object",
"properties": {
"path": {
"type": "string"
}
},
"additionalProperties": false,
"required": [
"path"
]
},
{
"type": "object",
"properties": {
"method": {
"type": "string"
}
},
"additionalProperties": false,
"required": [
"method"
]
},
{
"title": "feature.network.incoming.inner_filter.body_filter {#feature-network-incoming-inner-body-filter}",
"description": "Matches the request based on the contents of its body. Currently only JSON body filtering is\nsupported.",
"$ref": "#/$defs/BodyFilter"
},
{
"title": "feature.network.incoming.inner_filter.header_filter_jq",
"description": "##### {#feature-network-incoming-inner-header-filter-jq}\n\nSupports jq expressions, matches when the expression returns\n`true`. The expression is evaluated on each present header in\nthe request, in `HeaderKey: HeaderValue` format.",
"type": "object",
"properties": {
"query": {
"type": "string"
}
},
"additionalProperties": false,
"required": [
"query"
]
}
]
},
"InnerMessageFilter": {
"description": "One filter inside `all_of` / `any_of`. Only `metadata` regexes for now; the list form leaves\nroom for other leaf kinds without changing the shape.",
"anyOf": [
{
"description": "A regex matched against each message attribute rendered as `<name>: <value>`.",
"type": "object",
"properties": {
"metadata": {
"type": "string"
}
},
"additionalProperties": false,
"required": [
"metadata"
]
}
]
},
"InternalProxyFileConfig": {
"description": "Configuration for the internal proxy mirrord spawns for each local mirrord session\nthat local layers use to connect to the remote agent\n\nThis is seldom used, but if you get `ConnectionRefused` errors, you might\nwant to increase the timeouts a bit.\n\n```json\n{\n \"internal_proxy\": {\n \"start_idle_timeout\": 30,\n \"idle_timeout\": 5\n }\n}\n```",
"type": "object",
"properties": {
"idle_timeout": {
"title": "internal_proxy.idle_timeout {#internal_proxy-idle_timeout}",
"description": "How much time to wait while we don't have any active connections before exiting.\n\nCommon cases would be running a chain of processes that skip using the layer\nand don't connect to the proxy.\n\n```json\n{\n \"internal_proxy\": {\n \"idle_timeout\": 30\n }\n}\n```",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
},
"json_log": {
"title": "internal_proxy.json_log {#internal_proxy-json_log}",
"description": "Whether the proxy should output logs in JSON format. If false, logs are output in\nhuman-readable format.\n\nDefaults to true.",
"type": [
"boolean",
"null"
]
},
"log_destination": {
"title": "internal_proxy.log_destination {#internal_proxy-log_destination}",
"description": "Set the log destination for the internal proxy.\n\n1. If the provided path ends with a separator (`/` on UNIX, `\\` on Windows), it will be\n treated as a path to directory where the log file should be created.\n2. Otherwise, if the path exists, mirrord will check if it's a directory or not.\n3. Otherwise, it will be treated as a path to the log file.\n\nmirrord will auto create all parent directories.\n\nDefaults to a randomized path inside the temporary directory.",
"type": [
"string",
"null"
]
},
"log_level": {
"title": "internal_proxy.log_level {#internal_proxy-log_level}",
"description": "Set the log level for the internal proxy.\n\nThe value should follow the RUST_LOG convention (i.e `mirrord=trace`).\n\nDefaults to `mirrord=info,warn`.",
"type": [
"string",
"null"
]
},
"ping_interval": {
"title": "internal_proxy.ping_interval {#internal_proxy-ping_interval}",
"description": "How often the internal proxy should ping the agent connection, in seconds.\n\nSmaller values detect dropped operator connections faster, at the cost of more frequent\nping traffic.\n\n```json\n{\n \"internal_proxy\": {\n \"ping_interval\": 5\n }\n}\n```",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
},
"process_logging_interval": {
"title": "internal_proxy.process_logging_interval {#internal_proxy-process_logging_interval}",
"description": "How often to log information about connected processes in seconds.\n\nThis feature logs details about processes that are currently connected to the internal\nproxy, including their PID, process name, command line, and connection status.\n\n```json\n{\n \"internal_proxy\": {\n \"process_logging_interval\": 60\n }\n}\n```",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
},
"socket_timeout": {
"description": "<!--${internal}-->\n\nSometimes the cpu is too busy with other tasks and the internal proxy sockets end\nup timing out. It's set at a ridiculous high value to prevent this from happening\nwhen a user hits a breakpoint while debugging, and stays stopped for a while, which\nsometimes results in mirrord not working when they resume.\n\n```json\n{\n \"internal_proxy\": {\n \"socket_timeout\": 31536000\n }\n}\n```",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
},
"start_idle_timeout": {
"title": "internal_proxy.start_idle_timeout {#internal_proxy-start_idle_timeout}",
"description": "How much time to wait for the first connection to the proxy in seconds.\n\nCommon cases would be running with dlv or any other debugger, which sets a breakpoint\non process execution, delaying the layer startup and connection to proxy.\n\n```json\n{\n \"internal_proxy\": {\n \"start_idle_timeout\": 60\n }\n}\n```",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
}
},
"additionalProperties": false
},
"JobTarget": {
"type": "object",
"properties": {
"container": {
"type": [
"string",
"null"
]
},
"job": {
"type": "string"
}
},
"additionalProperties": false,
"required": [
"job"
]
},
"KafkaPayloadProtobuf": {
"title": "feature.split_queues.{}.payload_protobuf {#feature-split_queues-queue_id-payload_protobuf}",
"description": "Only supported with `queue_type` of `Kafka`.\n\nDecodes the raw protobuf bytes in the message payload before the `jq_filter` runs, for\ntopics that carry plain protobuf (for example CDC events) instead of JSON. The decoded\nmessage is exposed to the jq program as an extra `payload_decoded` field, so filters can\ntarget schema fields directly:\n\n```json\n{\n \"queue_type\": \"Kafka\",\n \"payload_protobuf\": {\n \"schema_file\": \"schemas/cdc_record.proto\",\n \"message_type\": \"com.example.cdc.Record\"\n },\n \"jq_filter\": \".payload_decoded.merchant_id == 2137\"\n}\n```\n\nThe schema is compiled locally by the mirrord CLI (resolving imports on your machine), so\nthe operator never needs access to your `.proto` files. Field names appear in\n`payload_decoded` exactly as written in the schema, enum values as their names, and 64-bit\nintegers as JSON numbers. Fields at their default value are included. Messages that fail to\ndecode with the given schema never match the filter and stay with the deployed application.\n\nThe payload must be plain protobuf: schema-registry framing (magic byte + schema id prefix)\nis not supported.",
"type": "object",
"properties": {
"descriptor_base64": {
"description": "Base64-encoded serialized `FileDescriptorSet`, optionally gzip-compressed (plain\n`protoc --descriptor_set_out --include_imports` output works as-is). An alternative to\n`schema_file` for pre-compiled schemas; filled in automatically from `schema_file`\nduring config resolution.",
"type": [
"string",
"null"
]
},
"include_directories": {
"description": "Extra import roots for compiling `schema_file`. The file's own directory is always an\nimport root.",
"type": "array",
"items": {
"type": "string"
}
},
"message_type": {
"description": "Fully-qualified name of the payload's message type, e.g. `com.example.cdc.Record`.",
"type": "string"
},
"schema_file": {
"description": "Path to the `.proto` file defining the payload's message type. Relative paths are\nresolved against the current working directory. Not needed when `descriptor_base64` is\nprovided.",
"type": [
"string",
"null"
]
}
},
"additionalProperties": false,
"required": [
"message_type"
]
},
"LabelTarget": {
"description": "Selects every pod in the target namespace that has all configured labels.",
"type": "object",
"properties": {
"container": {
"type": [
"string",
"null"
]
},
"labels": {
"description": "Exact-match labels used to select pods. All entries must match.",
"type": "object",
"additionalProperties": {
"type": "string"
}
}
},
"additionalProperties": false,
"required": [
"labels"
]
},
"LatencyFileConfig": {
"description": "Configuration for adding artificial latency to outgoing network operations.\nUseful for testing application behavior under network delay conditions.",
"type": "object",
"properties": {
"receive_delay": {
"title": "_experimental_ latency.receive_delay {#experimental-latency-receive_delay}",
"description": "Delay in milliseconds for outgoing receive operations (Agent → Layer).\n\nDefaults to `0` (no delay).",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
},
"transmit_delay": {
"title": "_experimental_ latency.transmit_delay {#experimental-latency-transmit_delay}",
"description": "Delay in milliseconds for outgoing send operations (Layer → Agent).\n\nDefaults to `0` (no delay).",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
}
},
"additionalProperties": false
},
"LocalTlsDelivery": {
"description": "Stolen TLS traffic can be delivered to the local application either as TLS or as plain TCP.\nNote that stealing TLS traffic requires mirrord Operator support.\n\nTo have the stolen TLS traffic delivered with plain TCP, use:\n\n```json\n{\n \"protocol\": \"tcp\"\n}\n```\n\nTo have the traffic delivered with TLS, use:\n```json\n{\n \"protocol\": \"tls\"\n}\n```\n\nBy default, the local mirrord TLS client will trust any certificate presented by the local\napplication's TLS server. To override this behavior, you can either:\n\n1. Specify a list of paths to trust roots. These paths can lead either to PEM files or PEM file\n directories. Each found certificate will be used as a trust anchor.\n2. Specify a path to the cartificate chain used by the server.\n\nExample with trust roots:\n```json\n{\n \"protocol\": \"tls\",\n \"trust_roots\": [\"/path/to/cert.pem\", \"/path/to/cert/dir\"]\n}\n```\n\nExample with certificate chain:\n```json\n{\n \"protocol\": \"tls\",\n \"server_cert\": \"/path/to/cert.pem\"\n}\n```\n\nTo make a TLS connection to the local application's server,\nmirrord's TLS client needs a server name. You can supply it manually like this:\n```json\n{\n \"protocol\": \"tls\",\n \"server_name\": \"my.test.server.name\"\n}\n```\n\nIf you don't supply the server name:\n\n1. If `server_cert` is given, and the found end-entity certificate contains a valid server name,\n this server name will be used;\n2. Otherwise, if the original client supplied an SNI extension, the server name from that\n extension will be used;\n3. Otherwise, if the stolen request's URL contains a valid server name, that server name will be\n used;\n4. Otherwise, `localhost` will be used.\n\nIf the local application's TLS server requires a client certificate (mutual TLS), give\nmirrord's TLS client one to present:\n```json\n{\n \"protocol\": \"tls\",\n \"client_cert\": \"/path/to/client.cert.pem\",\n \"client_key\": \"/path/to/client.key.pem\"\n}\n```\n\nIn preview sessions (`mirrord preview start`) the mirrord operator makes the TLS connection to\nthe preview pod, so `client_cert` and `client_key` are read locally, stored in the session's\nSecret and presented by the operator. `server_name` is used the same way. The other settings\ndo not apply to previews: the operator always delivers over TLS and does not verify the\npreview pod's certificate.",
"type": "object",
"properties": {
"client_cert": {
"title": "feature.network.incoming.tls_delivery.client_cert {#feature-network-incoming-tls_delivery-client_cert}",
"description": "Path to a PEM file containing the certificate chain mirrord presents to the local\napplication's TLS server, for applications that require a client certificate.\n\nThis file must contain at least one certificate. Must be set together with `client_key`.",
"type": [
"string",
"null"
]
},
"client_key": {
"title": "feature.network.incoming.tls_delivery.client_key {#feature-network-incoming-tls_delivery-client_key}",
"description": "Path to a PEM file containing the private key of `client_cert`.\n\nThis file must contain exactly one private key. Must be set together with `client_cert`.",
"type": [
"string",
"null"
]
},
"protocol": {
"title": "feature.network.incoming.tls_delivery.protocol {#feature-network-incoming-tls_delivery-protocol}",
"description": "Protocol to use when delivering the TLS traffic locally. Defaults to `tls`.",
"$ref": "#/$defs/TlsDeliveryProtocol",
"default": "tls"
},
"server_cert": {
"description": "Path to a PEM file containing the certificate chain used by the local application's\nTLS server.\n\nThis file must contain at least one certificate.\nIt can contain entries of other types, e.g private keys, which are ignored.",
"type": [
"string",
"null"
]
},
"server_name": {
"title": "feature.network.incoming.tls_delivery.server_name {#feature-network-incoming-tls_delivery-server_name}",
"description": "Server name to use when making a connection.\n\nMust be a valid DNS name or an IP address.",
"type": [
"string",
"null"
]
},
"trust_roots": {
"title": "feature.network.incoming.tls_delivery.trust_roots {#feature-network-incoming-tls_delivery-trust_roots}",
"description": "Paths to PEM files and directories with PEM files containing allowed root certificates.\n\nDirectories are not traversed recursively.\n\nEach certificate found in the files is treated as an allowed root.\nThe files can contain entries of other types, e.g private keys, which are ignored.",
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
}
},
"additionalProperties": false
},
"MagicFileConfig": {
"description": "Sensible default behaviors that help most users. Disable individual flags only if they conflict\nwith your setup.\n\n```json\n{\n \"feature\": {\n \"magic\": {\n \"aws\": true\n }\n }\n}\n```",
"type": "object",
"properties": {
"auto_mount": {
"title": "feature.magic.auto_mount {#feature-magic-auto_mount}",
"description": "Kubernetes mounts ConfigMaps, Secrets, and volumes (e.g. PVCs) into the target container at\nfixed paths. When mirrord runs your process locally, those paths either don't exist or hold\nunrelated local data, so the process can't read the configuration or secrets it expects\nfrom the pod.\n\nWhen enabled, mirrord reads the target container's volume mounts from the remote pod spec\nand adds their mount paths to [`feature.fs.read_only`](#feature-fs-read_only), so the\nlocal process transparently reads those files from the remote pod while still writing\nlocally.\n\nThis matters when the file system mode is `localwithoverrides`: the mounted paths would\notherwise be served locally and not exist, so `auto_mount` forces them to be read from the\nremote pod. In the default `read` mode files are already read remotely, and in fully\n`local` mode all overrides are ignored, so `auto_mount` has no observable effect in\neither of those modes.\n\nPaths you've explicitly marked local via `feature.fs.local` are left untouched.\n\nDisable this only if you intentionally want the mounted paths served from the local file\nsystem.\n\nDefaults to `true`.",
"type": [
"boolean",
"null"
]
},
"aws": {
"title": "feature.magic.aws {#feature-magic-aws}",
"description": "The AWS CLI prefers local credentials (e.g. `~/.aws`, `AWS_PROFILE`) over the remote pod's\nidentity (IAM role, instance profile, IRSA). When those local credentials are present, the\npod's own identity is never used, which is rarely what you want in a mirrord session.\n\nWhen enabled, mirrord makes local AWS configuration unavailable to the process by:\n- Unsetting `AWS_PROFILE` and related AWS environment variables.\n- Mapping `~/.aws` to a temporary directory, so the AWS CLI cannot read local credentials\n and also has a writable location for its credential cache (avoiding errors on cache\n writes).\n\nThis allows the remote pod's IAM role / instance profile to be used as intended.\n\nDisable this only if you intentionally need local AWS credentials inside the local mirrord'\nprocess.\n\nDefaults to `true`.",
"type": [
"boolean",
"null"
]
},
"turbo": {
"title": "feature.magic.turbo {#feature-magic-turbo}",
"description": "Next.js + Turbopack runs transforms (e.g. PostCSS for CSS) in pooled Node worker\nsubprocesses, which talk to the parent over a loopback TCP channel (the parent listens on\n`127.0.0.1:0` and the worker connects back). With the mirrord layer loaded into the worker,\nthat loopback connection is routed through the agent (remote) by default, so the worker\nnever reaches the local parent and the build fails (e.g. `next dev` crashes compiling CSS).\n\nWhen enabled, mirrord detects Next.js processes and keeps their loopback IPC local by\nturning on\n[`feature.network.outgoing.ignore_localhost`](#feature-network-outgoing-ignore_localhost)\nfor the Next.js process and the Turbopack workers it spawns.\n\nDisable this only if you intentionally need outgoing localhost connections from a Next.js\nprocess to be routed to the remote pod.\n\nDefaults to `true`.",
"type": [
"boolean",
"null"
]
}
},
"additionalProperties": false
},
"MariadbBranchCopyConfig": {
"description": "Users can choose from the following copy mode to bootstrap their MariaDB branch database.\n\nAll copy modes accept `dump_args`. When this field is set, it replaces the default\n`mariadb-dump` arguments. The defaults are `--single-transaction` and `--no-tablespaces`;\ninclude them explicitly when overriding if you want to preserve the default behavior. An\nempty list means no dump args.\n\n- Empty\n\n Creates an empty database. If the source DB connection options are found from the chosen\n target, mirrord operator extracts the database name and create an empty DB. Otherwise, mirrord\n operator looks for the `name` field from the branch DB config object. This option is useful\n for users that run DB migrations themselves before starting the application.\n\n- Schema\n\n Creates an empty database and copies schema of all tables.\n\n- All\n\n Copies both schema and data of all tables. This option shall only be used when the data volume\n of the source database is minimal.",
"oneOf": [
{
"type": "object",
"properties": {
"dump_args": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
},
"mode": {
"type": "string",
"const": "empty"
},
"tables": {
"type": [
"object",
"null"
],
"additionalProperties": {
"$ref": "#/$defs/BranchItemCopyConfig"
}
}
},
"additionalProperties": false,
"required": [
"mode"
]
},
{
"type": "object",
"properties": {
"dump_args": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
},
"mode": {
"type": "string",
"const": "schema"
},
"tables": {
"type": [
"object",
"null"
],
"additionalProperties": {
"$ref": "#/$defs/BranchItemCopyConfig"
}
}
},
"additionalProperties": false,
"required": [
"mode"
]
},
{
"type": "object",
"properties": {
"dump_args": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
},
"mode": {
"type": "string",
"const": "all"
}
},
"additionalProperties": false,
"required": [
"mode"
]
}
]
},
"MessageFilterConfig": {
"title": "feature.split_queues.{}.filter {#feature-split_queues-queue_id-filter}",
"description": "A composable message filter, shaped like the HTTP `http_filter`: one `metadata` regex, or an\n`all_of` / `any_of` list of `metadata` regexes.\n\nA `metadata` regex is matched against every message attribute (SQS message attributes, Kafka\nand RabbitMQ headers, Pub/Sub attributes, Service Bus application properties, Temporal task\nmetadata, top-level JSON fields for Redis Pub/Sub and BullMQ) rendered as\n`<name>: <value>`, the same way the HTTP filter sees headers. The message matches when any\nattribute line matches, so one regex can target an attribute by name (`^tenant: blue$`) or\na value wherever it appears (`.*mirrord-session={{ key }}.*`). Matching is case sensitive.\n\nUse `filter` **or** the older `message_filter`, not both. `message_filter` is a map from an\nexact attribute name to a regex on its value, and is equivalent to an `all_of` of one\n`metadata` filter per entry.\n\n```json\n{\n \"feature\": {\n \"split_queues\": [\n {\n \"queue_id\": \"*\",\n \"queue_type\": \"SQS\",\n \"filter\": { \"metadata\": \"^tenant: blue-.*$\" }\n },\n {\n \"queue_id\": \"*\",\n \"queue_type\": \"Temporal\",\n \"filter\": {\n \"any_of\": [\n { \"metadata\": \"^header.baggage: .*mirrord-session={{ key }}.*$\" },\n { \"metadata\": \"^header.test: .*mirrord-session={{ key }}.*$\" }\n ]\n }\n }\n ]\n }\n}\n```",
"anyOf": [
{
"description": "A regex matched against each message attribute rendered as `<name>: <value>`. Supports\nthe syntax of the [`fancy-regex`](https://docs.rs/fancy-regex/latest/fancy_regex/)\ncrate.",
"type": "object",
"properties": {
"metadata": {
"type": "string"
}
},
"additionalProperties": false,
"required": [
"metadata"
]
},
{
"description": "The message must match every filter in the list. Cannot be empty.",
"type": "object",
"properties": {
"all_of": {
"type": "array",
"items": {
"$ref": "#/$defs/InnerMessageFilter"
},
"minItems": 1
}
},
"additionalProperties": false,
"required": [
"all_of"
]
},
{
"description": "The message must match at least one filter in the list. Cannot be empty.",
"type": "object",
"properties": {
"any_of": {
"type": "array",
"items": {
"$ref": "#/$defs/InnerMessageFilter"
},
"minItems": 1
}
},
"additionalProperties": false,
"required": [
"any_of"
]
}
]
},
"MongodbBranchCopyConfig": {
"description": "Users can choose from the following copy mode to bootstrap their MongoDB branch database:\n\n- Empty\n\n Creates an empty database. If the source DB connection options are found from the chosen\n target, mirrord operator extracts the database name and create an empty DB. Otherwise, mirrord\n operator looks for the `name` field from the branch DB config object. This option is useful\n for users that run DB migrations themselves before starting the application.\n\n- All\n\n Copies both schema and data of all collections. Supports optional collection filters\n to copy only specific collections or filter documents within collections.",
"oneOf": [
{
"type": "object",
"properties": {
"collections": {
"type": [
"object",
"null"
],
"additionalProperties": {
"$ref": "#/$defs/BranchItemCopyConfig"
}
},
"mode": {
"type": "string",
"const": "empty"
}
},
"additionalProperties": false,
"required": [
"mode"
]
},
{
"type": "object",
"properties": {
"collections": {
"description": "Optional collection filters. If not specified, all collections are copied.\nIf specified, only the listed collections are copied with their optional filters.",
"type": [
"object",
"null"
],
"additionalProperties": {
"$ref": "#/$defs/BranchItemCopyConfig"
}
},
"mode": {
"type": "string",
"const": "all"
}
},
"additionalProperties": false,
"required": [
"mode"
]
}
]
},
"MssqlBranchCopyConfig": {
"description": "Users can choose from the following copy mode to bootstrap their MSSQL branch database:\n\n- Empty\n\n Creates an empty database. If the source DB connection options are found from the chosen\n target, mirrord operator extracts the database name and create an empty DB. Otherwise, mirrord\n operator looks for the `name` field from the branch DB config object. This option is useful\n for users that run DB migrations themselves before starting the application.\n\n- Schema\n\n Creates an empty database and copies schema of all tables.\n\n- All\n\n Copies both schema and data of all tables. This option shall only be used\n when the data volume of the source database is minimal.",
"oneOf": [
{
"type": "object",
"properties": {
"mode": {
"type": "string",
"const": "empty"
},
"tables": {
"type": [
"object",
"null"
],
"additionalProperties": {
"$ref": "#/$defs/BranchItemCopyConfig"
}
}
},
"additionalProperties": false,
"required": [
"mode"
]
},
{
"type": "object",
"properties": {
"mode": {
"type": "string",
"const": "schema"
},
"tables": {
"type": [
"object",
"null"
],
"additionalProperties": {
"$ref": "#/$defs/BranchItemCopyConfig"
}
}
},
"additionalProperties": false,
"required": [
"mode"
]
},
{
"type": "object",
"properties": {
"mode": {
"type": "string",
"const": "all"
}
},
"additionalProperties": false,
"required": [
"mode"
]
}
]
},
"MysqlBranchCopyConfig": {
"description": "Users can choose from the following copy mode to bootstrap their MySQL branch database.\n\nAll copy modes accept `dump_args`. When this field is set, it replaces the default\n`mysqldump` arguments. The defaults are `--single-transaction` and `--no-tablespaces`;\ninclude them explicitly when overriding if you want to preserve the default behavior. An\nempty list means no dump args.\n\n- Empty\n\n Creates an empty database. If the source DB connection options are found from the chosen\n target, mirrord operator extracts the database name and create an empty DB. Otherwise, mirrord\n operator looks for the `name` field from the branch DB config object. This option is useful\n for users that run DB migrations themselves before starting the application.\n\n- Schema\n\n Creates an empty database and copies schema of all tables.\n\n- All\n\n Copies both schema and data of all tables. This option shall only be used when the data volume\n of the source database is minimal.",
"oneOf": [
{
"type": "object",
"properties": {
"dump_args": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
},
"mode": {
"type": "string",
"const": "empty"
},
"tables": {
"type": [
"object",
"null"
],
"additionalProperties": {
"$ref": "#/$defs/BranchItemCopyConfig"
}
}
},
"additionalProperties": false,
"required": [
"mode"
]
},
{
"type": "object",
"properties": {
"dump_args": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
},
"mode": {
"type": "string",
"const": "schema"
},
"tables": {
"type": [
"object",
"null"
],
"additionalProperties": {
"$ref": "#/$defs/BranchItemCopyConfig"
}
}
},
"additionalProperties": false,
"required": [
"mode"
]
},
{
"type": "object",
"properties": {
"dump_args": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
},
"mode": {
"type": "string",
"const": "all"
}
},
"additionalProperties": false,
"required": [
"mode"
]
}
]
},
"NetworkFileConfig": {
"description": "Controls mirrord network operations.\n\nSee the network traffic [reference](https://metalbear.com/mirrord/docs/reference/traffic/)\nfor more details.\n\n```json\n{\n \"feature\": {\n \"network\": {\n \"incoming\": {\n \"mode\": \"steal\",\n \"http_filter\": {\n \"header_filter\": \"^baggage: .*mirrord-session={{ key }}.*$\"\n },\n \"port_mapping\": [[ 7777, 8888 ]],\n \"ignore_localhost\": false,\n \"ignore_ports\": [9999, 10000]\n },\n \"outgoing\": {\n \"tcp\": true,\n \"udp\": true,\n \"filter\": {\n \"local\": [\"tcp://1.1.1.0/24:1337\", \"1.1.5.0/24\", \"google.com\", \":53\"]\n },\n \"ignore_localhost\": false,\n \"unix_streams\": \"bear.+\"\n },\n \"dns\": {\n \"enabled\": true,\n \"filter\": {\n \"local\": [\"1.1.1.0/24:1337\", \"1.1.5.0/24\", \"google.com\"]\n }\n }\n }\n }\n}\n```",
"type": "object",
"properties": {
"dns": {
"title": "feature.network.dns {#feature-network-dns}",
"anyOf": [
{
"$ref": "#/$defs/ToggleableConfig7"
},
{
"type": "null"
}
]
},
"incoming": {
"title": "feature.network.incoming {#feature-network-incoming}",
"anyOf": [
{
"$ref": "#/$defs/ToggleableConfig4"
},
{
"type": "null"
}
]
},
"ipv6": {
"title": "feature.network.ipv6 {#feature-network-ipv6}",
"description": "Enable IPv6 support, letting the application open IPv6 sockets and listen to or send\ntraffic over IPv6. Enabled by default. Set to `false` to make IPv6 socket creation fail\nwith `EAFNOSUPPORT`, like on a host without an IPv6 stack.",
"type": [
"boolean",
"null"
]
},
"outgoing": {
"title": "feature.network.outgoing {#feature-network-outgoing}",
"anyOf": [
{
"$ref": "#/$defs/ToggleableConfig6"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
"OutgoingFileConfig": {
"description": "Tunnel outgoing network operations through mirrord.\n\nSee the outgoing [reference](https://metalbear.com/mirrord/docs/reference/traffic/#outgoing) for more\ndetails.\n\nYou can use either the `true` or `false` values to turn outgoing traffic tunneling on or off.\n\n```json\n{\n \"feature\": {\n \"network\": {\n \"outgoing\": true\n }\n }\n}\n```\n\nAlternatively, you can use more fine-grained configuration.\n\n```json\n{\n \"feature\": {\n \"network\": {\n \"outgoing\": {\n \"tcp\": true,\n \"udp\": true,\n \"ignore_localhost\": false,\n \"filter\": {\n \"local\": [\"tcp://1.1.1.0/24:1337\", \"1.1.5.0/24\", \"google.com\", \":53\"]\n },\n \"unix_streams\": \"bear.+\"\n }\n }\n }\n}\n```",
"type": "object",
"properties": {
"filter": {
"title": "feature.network.outgoing.filter {#feature.network.outgoing.filter}",
"description": "Filters that are used to send specific traffic from either the remote pod or the local app",
"anyOf": [
{
"$ref": "#/$defs/OutgoingFilterConfig"
},
{
"type": "null"
}
]
},
"ignore_localhost": {
"title": "feature.network.outgoing.ignore_localhost {#feature.network.outgoing.ignore_localhost}",
"description": "Defaults to `false`.",
"type": [
"boolean",
"null"
]
},
"tcp": {
"title": "feature.network.outgoing.tcp {#feature.network.outgoing.tcp}",
"description": "Defaults to `true`.",
"type": [
"boolean",
"null"
]
},
"udp": {
"title": "feature.network.outgoing.udp {#feature.network.outgoing.udp}",
"description": "Defaults to `true`.",
"type": [
"boolean",
"null"
]
},
"unix_streams": {
"title": "feature.network.outgoing.unix_streams {#feature.network.outgoing.unix_streams}",
"description": "Connect to these unix streams remotely (and to all other paths locally).\n\nYou can either specify a single value or an array of values.\nEach value is interpreted as a regular expression\n([Supported Syntax](https://docs.rs/regex/1.7.1/regex/index.html#syntax)).\n\nWhen your application connects to a unix socket, the target address will be converted to a\nstring (non-utf8 bytes are replaced by a placeholder character) and matched against the set\nof regexes specified here. If there is a match, mirrord will connect your application with\nthe target unix socket address on the target pod. Otherwise, it will leave the connection\nto happen locally on your machine.",
"anyOf": [
{
"$ref": "#/$defs/VecOrSingle"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
"OutgoingFilterConfig": {
"description": "List of addresses/ports/subnets that should be sent through either the remote pod or local app,\ndepending how you set this up with either `remote` or `local`.\n\nYou may use this option to specify when outgoing traffic is sent from the remote pod (which\nis the default behavior when you enable outgoing traffic), or from the local app (default when\nyou have outgoing traffic disabled).\n\nTakes a list of values, such as:\n\n- Only UDP traffic on subnet `1.1.1.0/24` on port 1337 will go through the remote pod.\n\n```json\n{\n \"remote\": [\"udp://1.1.1.0/24:1337\"]\n}\n```\n\n- Only UDP and TCP traffic on resolved address of `google.com` on port `1337` and `7331` will go\n through the remote pod.\n```json\n{\n \"remote\": [\"google.com:1337\", \"google.com:7331\"]\n}\n```\n\n- Only TCP traffic on `localhost` on port 1337 will go through the local app, the rest will be\n emitted remotely in the cluster.\n\n```json\n{\n \"local\": [\"tcp://localhost:1337\"]\n}\n```\n\n- Only outgoing traffic on port `1337` and `7331` will go through the local app.\n```json\n{\n \"local\": [\":1337\", \":7331\"]\n}\n```\n\nValid values follow this pattern: `[protocol]://[name|address|subnet/mask]:[port]`.\n\nHost names listed here are automatically mirrored into\n[`feature.network.dns.filter`](super::dns::DnsFilterConfig) on the matching side, so the\nname is resolved on the side that handles the connection. For example,\n`{ \"local\": [\"some.domain.com\"] }` implies `dns.filter.local = [\"some.domain.com\"]` even if\n`dns.filter` is otherwise unset.",
"oneOf": [
{
"description": "When filters are specified under `remote`, matching traffic will go through the remote pod,\neverything else will go through local.",
"type": "object",
"properties": {
"remote": {
"$ref": "#/$defs/VecOrSingle"
}
},
"additionalProperties": false,
"required": [
"remote"
]
},
{
"description": "When filters are specified under `local`, matching traffic will go through the local app,\neverything else will go through the remote pod.",
"type": "object",
"properties": {
"local": {
"$ref": "#/$defs/VecOrSingle"
}
},
"additionalProperties": false,
"required": [
"local"
]
}
]
},
"PgAdditionalDatabaseConfig": {
"description": "<!--${internal}-->\nOne more database copied into a PostgreSQL branch. Documented on\n`PgBranchConfig::additional_databases`.",
"type": "object",
"properties": {
"connection": {
"description": "How the application connects to this database. mirrord points it at the branch.",
"anyOf": [
{
"$ref": "#/$defs/DbBranchingConnectionSource"
},
{
"type": "null"
}
]
},
"copy": {
"description": "Copy mode and table filters for this database.",
"$ref": "#/$defs/PgBranchCopyConfig",
"default": {
"mode": "empty",
"tables": null
}
},
"name": {
"description": "Database name on the source server, reused on the branch.",
"type": "string"
}
},
"additionalProperties": false,
"required": [
"name"
]
},
"PgBranchCopyConfig": {
"description": "Users can choose from the following copy mode to bootstrap their PostgreSQL branch database.\n\nAll copy modes accept `dump_args`. When this field is set, it replaces the default `pg_dump`\narguments. The defaults are `--no-owner` and `--no-acl`; include them explicitly when\noverriding if you want to preserve the default behavior. An empty list means no dump args.\n\n- Empty\n\n Creates an empty database. If the source DB connection options are found from the chosen\n target, mirrord operator extracts the database name and create an empty DB. Otherwise, mirrord\n operator looks for the `name` field from the branch DB config object. This option is useful\n for users that run DB migrations themselves before starting the application.\n\n- Schema\n\n Creates an empty database and copies schema of all tables.\n\n- All\n\n Copies both schema and data of all tables. This option shall only be used when the data volume\n of the source database is minimal.",
"oneOf": [
{
"type": "object",
"properties": {
"dump_args": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
},
"mode": {
"type": "string",
"const": "empty"
},
"tables": {
"type": [
"object",
"null"
],
"additionalProperties": {
"$ref": "#/$defs/BranchItemCopyConfig"
}
}
},
"additionalProperties": false,
"required": [
"mode"
]
},
{
"type": "object",
"properties": {
"dump_args": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
},
"mode": {
"type": "string",
"const": "schema"
},
"tables": {
"type": [
"object",
"null"
],
"additionalProperties": {
"$ref": "#/$defs/BranchItemCopyConfig"
}
}
},
"additionalProperties": false,
"required": [
"mode"
]
},
{
"type": "object",
"properties": {
"dump_args": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
},
"mode": {
"type": "string",
"const": "all"
}
},
"additionalProperties": false,
"required": [
"mode"
]
}
]
},
"PodTarget": {
"description": "<!--${internal}-->\nMirror the pod specified by [`PodTarget::pod`].",
"type": "object",
"properties": {
"container": {
"type": [
"string",
"null"
]
},
"pod": {
"description": "<!--${internal}-->\nPod to mirror.",
"type": "string"
}
},
"additionalProperties": false,
"required": [
"pod"
]
},
"PreviewCronJobFileConfig": {
"description": "CronJob-target settings for preview sessions.\n\n```json\n{\n \"target\": \"cronjob/nightly-scan\",\n \"feature\": {\n \"preview\": {\n \"image\": \"myrepo/scan:pr-4821\",\n \"cronjob\": {\n \"schedule\": \"*/30 * * * *\"\n }\n }\n }\n}\n```",
"type": "object",
"properties": {
"schedule": {
"title": "feature.preview.cronjob.schedule {#feature-preview-cronjob-schedule}",
"description": "Cron schedule for the preview CronJob, in Kubernetes CronJob syntax\n(`\"0 * * * *\"`, `\"@hourly\"`, ...). When omitted, the preview inherits the source\nCronJob's schedule.\n\nWhatever the schedule, the preview CronJob is also triggered once right after the\nsession starts unless [`trigger_on_start`](#feature-preview-cronjob-trigger_on_start)\nis `false`.",
"type": [
"string",
"null"
]
},
"trigger_on_start": {
"title": "feature.preview.cronjob.trigger_on_start {#feature-preview-cronjob-trigger_on_start}",
"description": "Run the preview CronJob once right after the session starts, regardless of its schedule,\nso you see a run without waiting for the next scheduled time. Defaults to `true`.\n\nSet to `false` for jobs whose timing matters (a report that must only run in its window,\na job that assumes the previous scheduled run finished), so the preview runs on the\nschedule alone.",
"type": [
"boolean",
"null"
]
}
},
"additionalProperties": false
},
"PreviewFileConfig": {
"description": "Controls the lifetime and creation behavior of preview sessions.\n\n```json\n{\n \"feature\": {\n \"preview\": {\n \"image\": \"my-registry/my-app:latest\",\n \"ttl_mins\": 60,\n \"creation_timeout_secs\": 60\n }\n }\n}\n```",
"type": "object",
"properties": {
"config_mounts": {
"title": "feature.preview.config_mounts {#feature-preview-config_mounts}",
"description": "Files to mount into the preview pod at session start.\n\nEach entry projects a single file at an absolute path inside the\ncontainer's filesystem, without shadowing the surrounding directory's\nother contents. Useful for overriding individual config files\n(`/etc/app/config.yaml`, `/etc/nginx/conf.d/custom.conf`, ...) without\nrebuilding the image.\n\nFiles are read once at session creation and never refreshed — re-run\n`mirrord preview start` with the same `key` to change them.\n\n`config_mounts` is not a confidentiality boundary: the `payload` is\nstored on the `PreviewSession` resource and is visible to anyone with\n`get` permission on it. Do not put credentials or secrets here.\n\nEach entry sources its content one of two ways:\n- **Inline:** set `type` (`\"text\"` or `\"binary\"`) and `payload` directly.\n- **From file:** set `from_file` to a path on the local filesystem. The file is read at\n session creation time; valid UTF-8 contents are sent as `\"text\"`, anything else is\n base64-encoded and sent as `\"binary\"`. The local path is not transmitted to the operator.\n\n`payload`/`type` and `from_file` are mutually exclusive on a single entry.\n\n```json\n{\n \"feature\": {\n \"preview\": {\n \"config_mounts\": [\n {\n \"mount_at\": \"/etc/app/config.yaml\",\n \"type\": \"text\",\n \"payload\": \"server:\\n listen: 8080\\n\"\n },\n {\n \"mount_at\": \"/usr/local/bin/probe\",\n \"from_file\": \"./build/probe\"\n }\n ]\n }\n }\n}\n```\n\n##### Implementation\n\nEach session creates one `ConfigMap` owned by the `PreviewSession`,\nholding all mount payloads. The ConfigMap is mounted into the preview pod\nwith one per-file `subPath` bind per entry, so each mount overlays a\nsingle path without shadowing the surrounding directory. An entry inside\na ConfigMap, Secret, downward API, or projected volume the container\nalready mounts is projected into that volume instead. The ConfigMap is\ngarbage-collected automatically when the session ends.\n\nBecause both the `PreviewSession` resource and the generated `ConfigMap`\nlive in etcd, the **combined size of all `payload` payloads in a single\nsession is bound by Kubernetes' ~1 MiB per-object limit**. For `\"text\"`\npayloads that's roughly 1 MiB of content; for `\"binary\"`, base64\ninflates the encoded form by ~33%, leaving roughly 750 KiB of raw bytes.\nExceeding the limit causes the apiserver to reject the session at\ncreation time.",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/$defs/ConfigMount"
}
},
"creation_timeout_secs": {
"title": "feature.preview.creation_timeout_secs {#feature-preview-creation_timeout_secs}",
"description": "How long (in seconds) the CLI waits for the preview session to become ready.\nIf the session hasn't reached `Ready` within this time, the CLI deletes it.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
},
"cronjob": {
"title": "feature.preview.cronjob {#feature-preview-cronjob}",
"description": "Settings that only apply when the preview target is a `cronjob/<name>`.\n\nA CronJob preview is an isolated copy of the source CronJob running your image, with the\nsame env, DB branch, and mount overrides other previews get. It is triggered once right\nafter the session starts (unless `trigger_on_start` is `false`), and then keeps running\non its schedule until the session ends.",
"anyOf": [
{
"$ref": "#/$defs/PreviewCronJobFileConfig"
},
{
"type": "null"
}
]
},
"idle": {
"title": "feature.preview.idle {#feature-preview-idle}",
"description": "Idle-mode settings: run the preview with zero pods while it receives no traffic,\nand boot them back up when traffic arrives.",
"anyOf": [
{
"$ref": "#/$defs/PreviewIdleFileConfig"
},
{
"type": "null"
}
]
},
"image": {
"title": "feature.preview.image {#feature-preview-image}",
"description": "Container image to run in the preview pod.\nThe image must be pre-built and pushed to a registry accessible by the cluster.",
"type": [
"string",
"null"
]
},
"labels": {
"title": "feature.preview.labels {#feature-preview-labels}",
"description": "Filters labels copied from the target pod template to the preview pod.",
"anyOf": [
{
"$ref": "#/$defs/PreviewLabelsFileConfig"
},
{
"type": "null"
}
]
},
"replicas": {
"title": "feature.preview.replicas {#feature-preview-replicas}",
"description": "Number of preview pods to deploy.",
"type": [
"integer",
"null"
],
"format": "int32"
},
"secret_mounts": {
"title": "feature.preview.secret_mounts {#feature-preview-secret_mounts}",
"description": "Files to mount into the preview pod at session start, stored as a\nKubernetes `Secret` instead of on the `PreviewSession` resource.\n\nSame shape and behavior as [`config_mounts`](#feature-preview-config_mounts):\neach entry projects a single file at an absolute path inside the\ncontainer's filesystem without shadowing the surrounding directory, and\nfiles are read once at session creation and never refreshed.\n\nUse `secret_mounts` for sensitive files (credentials, connection\nstrings, ...). Unlike `config_mounts`, the file contents are never stored\non the `PreviewSession` resource. The CLI sends them to the operator,\nwhich creates a `Secret` holding them; the session stores only where each\nfile mounts, not the contents. Access to the contents can then be\ncontrolled via RBAC on the `Secret` independently of who can read the\nsession.\n\nEach entry sources its content one of two ways:\n- **Inline:** set `type` (`\"text\"` or `\"binary\"`) and `payload` directly.\n- **From file:** set `from_file` to a path on the local filesystem. The file is read at\n session creation time; valid UTF-8 contents are sent as `\"text\"`, anything else is\n base64-encoded and sent as `\"binary\"`. The local path is not transmitted to the operator.\n\n`payload`/`type` and `from_file` are mutually exclusive on a single entry.\n\n```json\n{\n \"feature\": {\n \"preview\": {\n \"secret_mounts\": [\n {\n \"mount_at\": \"/app/appsettings.ci.json\",\n \"from_file\": \"./appsettings.ci.json\"\n }\n ]\n }\n }\n}\n```\n\n##### Implementation\n\nThe operator creates one `Secret` per session, holding all mount contents,\nwith its own service account - so the developer running the CLI does not\nneed permission to create `Secret`s. The `Secret` carries the session's\nowner reference and is garbage-collected when the session ends. It is\nmounted into the preview pod with one per-file `subPath` bind per entry, so\neach mount overlays a single path without shadowing the surrounding\ndirectory. An entry inside a ConfigMap, Secret, downward API, or projected\nvolume the container already mounts is projected into that volume instead.\n\nThe same ~1 MiB per-object Kubernetes limit as `config_mounts` applies to\nthe combined size of all contents in a single session.",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/$defs/ConfigMount"
}
},
"ttl_mins": {
"title": "feature.preview.ttl_mins {#feature-preview-ttl_mins}",
"description": "How long (in minutes) the preview session is allowed to live after creation.\nThe operator will terminate the session when this time elapses.\n\nSet to `\"infinite\"` to disable TTL.\n\nMutually exclusive with [`ttl_secs`](#feature-preview-ttl_secs); when neither is set,\ndefaults to 60 minutes.",
"anyOf": [
{
"$ref": "#/$defs/PreviewTtl"
},
{
"type": "null"
}
]
},
"ttl_secs": {
"title": "feature.preview.ttl_secs {#feature-preview-ttl_secs}",
"description": "Same as [`ttl_mins`](#feature-preview-ttl_mins) but expressed in seconds.\n\nSet to `\"infinite\"` to disable TTL.\n\nMutually exclusive with [`ttl_mins`](#feature-preview-ttl_mins).",
"anyOf": [
{
"$ref": "#/$defs/PreviewTtl"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
"PreviewIdleFileConfig": {
"description": "Idle-mode settings for preview sessions.\n\nAn idle preview keeps its session, traffic listener, queue splits, and database branches\nalive, but runs zero pods. The first stolen request or routed queue message boots the pods\nback up. HTTP requests arriving while the pods boot are held by the operator until a pod is\nready or the wake timeout expires.",
"type": "object",
"properties": {
"sleep_after_secs": {
"title": "feature.preview.idle.sleep_after_secs {#feature-preview-idle-sleep_after_secs}",
"description": "Scale the preview pods to zero after this many seconds without traffic.\nMust be at least 30. When unset, the session never idles automatically.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
},
"start_idle": {
"title": "feature.preview.idle.start_idle {#feature-preview-idle-start_idle}",
"description": "Start the preview session with zero pods. The first matching request or queue message\nboots them.",
"type": [
"boolean",
"null"
]
},
"wake_timeout_secs": {
"title": "feature.preview.idle.wake_timeout_secs {#feature-preview-idle-wake_timeout_secs}",
"description": "How long a waking session holds incoming requests while waiting for a preview pod to\nbecome ready, before letting them fail. Defaults to the operator's default (90 seconds).\n\nA pod scheduled onto a node that has to pull its image first can take longer than the\ndefault, and the held requests then fail; raise this value (or pre-pull the image) for\nsuch setups.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
}
},
"additionalProperties": false
},
"PreviewLabelsFileConfig": {
"type": "object",
"properties": {
"exclude": {
"title": "feature.preview.labels.exclude {#feature-preview-labels-exclude}",
"description": "Include labels from the target pod template whose keys do **NOT** match this option.\nLabel keys can be matched using `*` and `?`, where `?` matches exactly one character and\n`*` matches any number of characters (including zero).\n\nCan be passed as a single value or as a list.",
"anyOf": [
{
"$ref": "#/$defs/VecOrSingle"
},
{
"type": "null"
}
]
},
"include": {
"title": "feature.preview.labels.include {#feature-preview-labels-include}",
"description": "Include only these labels from the target pod template in the preview pod.\nLabel keys can be matched using `*` and `?`, where `?` matches exactly one character and\n`*` matches any number of characters (including zero).\n\nCan be passed as a single value or as a list.",
"anyOf": [
{
"$ref": "#/$defs/VecOrSingle"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
"PreviewTtl": {
"description": "A TTL value usable for either [`PreviewConfig::ttl_mins`] or [`PreviewConfig::ttl_secs`].\n\nThe unit (minutes or seconds) depends on which field carries the value; the variants\nthemselves are unit-agnostic.",
"anyOf": [
{
"type": "integer",
"format": "uint64",
"minimum": 0
},
{
"$ref": "#/$defs/PreviewTtlKeyword"
}
]
},
"PreviewTtlKeyword": {
"type": "string",
"enum": [
"infinite"
]
},
"QueueKind": {
"title": "feature.split_queues.{}.queue_type {#feature-split_queues-queue_id-queue_type}",
"description": "The broker the queue lives on. One of `SQS`, `Kafka`, `RMQ`, `GCPPubSub`, `RedisPubSub`,\n`AzureServiceBus`, `Temporal`, `BullMQ`, `NATS`, or `NATSPubSub`.\n\nThe `Display` form is the snake_case name used in analytics keys and logs.",
"oneOf": [
{
"type": "string",
"enum": [
"SQS",
"Kafka",
"RMQ",
"GCPPubSub",
"RedisPubSub",
"AzureServiceBus",
"Temporal",
"BullMQ",
"NATS",
"NATSPubSub"
]
}
]
},
"QueueMode": {
"title": "feature.split_queues.{}.queue_mode {#feature-split_queues-queue_id-queue_mode}",
"description": "Controls what happens to a message that matches this session's filter.\n\n- `steal` (default): the matched message is delivered only to this session, so the deployed\n application never sees it.\n- `mirror`: the matched message is delivered to this session **and** still delivered to the\n deployed application, so both process a copy.",
"oneOf": [
{
"description": "Take matched messages away from the deployed application (current behavior).",
"type": "string",
"const": "steal"
},
{
"description": "Deliver matched messages to this session while the deployed application still gets them.",
"type": "string",
"const": "mirror"
}
]
},
"RedisBranchCopyConfig": {
"description": "How a Redis branch is seeded from its source.\n\n- `empty` (default): Start a fresh, empty instance.\n- `all`: Copy keys from the source instance. Optional `patterns` are `SCAN MATCH` globs limiting\n which keys are copied; omitting them copies the whole keyspace.",
"oneOf": [
{
"type": "object",
"properties": {
"mode": {
"type": "string",
"const": "empty"
}
},
"additionalProperties": false,
"required": [
"mode"
]
},
{
"type": "object",
"properties": {
"mode": {
"type": "string",
"const": "all"
},
"patterns": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
}
},
"additionalProperties": false,
"required": [
"mode"
]
}
]
},
"RedisConnectionConfig": {
"description": "Supports either a complete URL or separated connection parameters.\nIf both are provided, `url` takes precedence.\n\nThe following fields can be sourced via remote environment variable:\n- url\n- host\n- password\n- username\n\nExample:\n```json\n\"connection\": {\n \"host\": { \"type\": \"env\", \"variable\": \"REDIS_HOST\" },\n \"port\": 6379,\n \"password\": { \"type\": \"env\", \"variable\": \"REDIS_PASSWORD\" }\n}\n```",
"type": "object",
"properties": {
"database": {
"title": "feature.db_branches[].connection.database (type: redis)",
"description": "Redis database number (default: 0).",
"type": [
"integer",
"null"
],
"format": "uint16",
"default": null,
"maximum": 65535,
"minimum": 0
},
"host": {
"title": "feature.db_branches[].connection.host (type: redis)",
"description": "Redis host/hostname.\nCan be sourced from an environment variable.",
"anyOf": [
{
"$ref": "#/$defs/RedisValueSource"
},
{
"type": "null"
}
],
"default": null
},
"password": {
"title": "feature.db_branches[].connection.password (type: redis)",
"description": "Redis password for authentication.\nCan be sourced from an environment variable.",
"anyOf": [
{
"$ref": "#/$defs/RedisValueSource"
},
{
"type": "null"
}
],
"default": null
},
"port": {
"title": "feature.db_branches[].connection.port (type: redis)",
"description": "Redis port (default: 6379).",
"type": [
"integer",
"null"
],
"format": "uint16",
"default": null,
"maximum": 65535,
"minimum": 0
},
"tls": {
"title": "feature.db_branches[].connection.tls (type: redis)",
"description": "Enable TLS/SSL connection.",
"type": [
"boolean",
"null"
],
"default": null
},
"url": {
"title": "feature.db_branches[].connection.url (type: redis)",
"description": "Complete Redis URL (e.g., `redis://user:pass@host:6379/0`).\nCan be sourced from an environment variable.",
"anyOf": [
{
"$ref": "#/$defs/RedisValueSource"
},
{
"type": "null"
}
],
"default": null
},
"username": {
"title": "feature.db_branches[].connection.username (type: redis)",
"description": "Redis username (Redis 6+ ACL).\nCan be sourced from an environment variable.",
"anyOf": [
{
"$ref": "#/$defs/RedisValueSource"
},
{
"type": "null"
}
],
"default": null
}
},
"additionalProperties": false
},
"RedisEnvSource": {
"description": "<!--${internal}-->\nEnvironment variable source for Redis values.",
"type": "object",
"properties": {
"container": {
"type": [
"string",
"null"
],
"default": null
},
"type": {
"$ref": "#/$defs/RedisEnvSourceType"
},
"variable": {
"type": "string"
}
},
"additionalProperties": false,
"required": [
"type",
"variable"
]
},
"RedisEnvSourceType": {
"description": "<!--${internal}-->\nType marker for environment variable sources.",
"type": "string",
"enum": [
"env"
]
},
"RedisLocalConfig": {
"description": "Configuration for local Redis runtime.",
"type": "object",
"properties": {
"container_command": {
"title": "feature.db_branches[].local.container_command (type: redis)",
"description": "Custom path to the container command.\nIf not provided, uses the runtime name from PATH (e.g., \"docker\").\nExample: `/usr/local/bin/docker` or `/home/user/.local/bin/podman`",
"type": [
"string",
"null"
],
"default": null
},
"container_runtime": {
"title": "feature.db_branches[].local.container_runtime (type: redis)",
"description": "Which container runtime to use (Docker, Podman, or nerdctl).\nOnly applies when `runtime` is `container` or `auto`.",
"$ref": "#/$defs/ContainerRuntime",
"default": "docker"
},
"options": {
"title": "feature.db_branches[].local.options (type: redis)",
"description": "Additional Redis configuration options.",
"$ref": "#/$defs/RedisOptions",
"default": {
"args": []
}
},
"port": {
"title": "feature.db_branches[].local.port (type: redis)",
"description": "Local port to bind Redis to (default: 6379).",
"type": "integer",
"format": "uint16",
"default": 6379,
"maximum": 65535,
"minimum": 0
},
"runtime": {
"title": "feature.db_branches[].local.runtime (type: redis)",
"description": "Runtime backend for local Redis: `container`, `redis_server`, or `auto`.",
"$ref": "#/$defs/RedisRuntime",
"default": "container"
},
"server_command": {
"title": "feature.db_branches[].local.server_command (type: redis)",
"description": "Custom path to the redis-server binary.\nIf not provided, uses \"redis-server\" from PATH.\nExample: `/opt/redis/bin/redis-server`",
"type": [
"string",
"null"
],
"default": null
},
"version": {
"title": "feature.db_branches[].local.version (type: redis)",
"description": "Redis version/tag to use (default: \"7-alpine\").\nUsed as the container image tag.",
"type": "string",
"default": "7-alpine"
}
},
"additionalProperties": false
},
"RedisOptions": {
"description": "Example:\n```json\n{\n \"args\": [\"--maxmemory\", \"256mb\", \"--appendonly\", \"yes\"]\n}\n```",
"type": "object",
"properties": {
"args": {
"description": "Raw arguments passed directly to redis-server or as Docker CMD args.\nUse standard Redis config syntax (e.g., \"--maxmemory 256mb\").",
"type": "array",
"default": [],
"items": {
"type": "string"
}
}
},
"additionalProperties": false
},
"RedisRuntime": {
"description": "For container-based runtimes, mirrord spawns the Redis image in a container.\nFor `redis_server`, it runs the native binary directly.\n\nBackends:\n- `container` (default) - Uses a container runtime (Docker/Podman/nerdctl), configured via\n `container_runtime`.\n- `redis_server` - Uses native redis-server binary\n- `auto` - Tries container first, falls back to redis-server",
"type": "string",
"enum": [
"container",
"redis_server",
"auto"
]
},
"RedisValueSource": {
"description": "<!--${internal}-->\nSource for a Redis configuration value.\n\nValues can be specified directly or sourced from environment variables.",
"anyOf": [
{
"type": "string"
},
{
"$ref": "#/$defs/RedisEnvSource"
}
]
},
"ReplicaSetTarget": {
"type": "object",
"properties": {
"container": {
"type": [
"string",
"null"
]
},
"replica_set": {
"type": "string"
}
},
"additionalProperties": false,
"required": [
"replica_set"
]
},
"RolloutTarget": {
"description": "<!--${internal}-->\nMirror the rollout specified by [`RolloutTarget::rollout`].",
"type": "object",
"properties": {
"container": {
"type": [
"string",
"null"
]
},
"rollout": {
"description": "<!--${internal}-->\nRollout to mirror.",
"type": "string"
}
},
"additionalProperties": false,
"required": [
"rollout"
]
},
"S3BranchCopyConfig": {
"description": "Users can choose from the following copy modes to bootstrap their S3 branch bucket:\n\n- Empty (default)\n\n Creates the branch bucket with no objects in it. Useful for apps that write their own\n fixtures, or when the source bucket is far too large to clone.\n\n- All\n\n Copies the objects of the source bucket into the branch bucket. Optional `objects` field\n is a list of regular expressions matched against object keys, selecting the objects to be\n copied. Omitting this field or passing an empty list copies all objects.\n\nThe copy runs in the provider's cloud - mirrord never streams the objects through the\ncluster - so a wide `all` costs provider-side copy time rather than local bandwidth.\n\n```json\n{ \"copy\": { \"mode\": \"all\", \"objects\": [\"^fixtures/.*\", \"\\\\.json$\"] } }\n```",
"oneOf": [
{
"type": "object",
"properties": {
"mode": {
"type": "string",
"const": "empty"
}
},
"additionalProperties": false,
"required": [
"mode"
]
},
{
"type": "object",
"properties": {
"mode": {
"type": "string",
"const": "all"
},
"objects": {
"description": "Regular expressions matched against the source objects' keys. An object is copied\nwhen it matches any of them. All objects are copied when this is not set.",
"type": "array",
"items": {
"type": "string"
}
}
},
"additionalProperties": false,
"required": [
"mode"
]
}
]
},
"S3Provider": {
"description": "Cloud provider hosting an S3 branch's source bucket.\n\nCurrently Amazon S3 is the only provider supported.\nThis config option exists just for forward compatibility.",
"oneOf": [
{
"description": "Amazon S3.",
"type": "string",
"const": "AWS"
}
]
},
"SecurityContext": {
"type": "object",
"properties": {
"app_armor_profile": {
"anyOf": [
{
"$ref": "#/$defs/SecurityProfile"
},
{
"type": "null"
}
]
},
"seccomp_profile": {
"anyOf": [
{
"$ref": "#/$defs/SecurityProfile"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
"SecurityProfile": {
"type": "object",
"properties": {
"localhost_profile": {
"type": [
"string",
"null"
]
},
"type": {
"type": "string"
}
},
"additionalProperties": false,
"required": [
"type"
]
},
"ServerlessTarget": {
"description": "Targets a serverless remote workload, i.e. an application running outside Kubernetes with a\n`mirrord-agent` companion in each replica, reached through sessions-manager.\n\n- `serverless/{service-name}[/container/{replica-id}]`;\n\nThe environment part of the sessions-manager service scope is not part of the target; it is\ntaken from `target.namespace`, defaulting to `default`.\n\nThe `container` field:\n\nRestricts the session to the agent on this replica, as its `ReplicaId` (the agent's\n`MIRRORD_REMOTE_SERVICE_REPLICA`, which defaults to the hosting platform's replica id such as\nthe ECS task ARN, and then to the container hostname). When unset, sessions-manager offers the\neligible agent with the lowest replica id.",
"type": "object",
"properties": {
"container": {
"type": [
"string",
"null"
]
},
"serverless": {
"description": "Name of the service in sessions-manager: the logical workload whose replicas\neach run an agent companion. Together with the environment it forms the\n`ServiceScope` that the session's agent pairing is restricted to.",
"type": "string"
}
},
"additionalProperties": false,
"required": [
"serverless"
]
},
"ServiceTarget": {
"type": "object",
"properties": {
"container": {
"type": [
"string",
"null"
]
},
"service": {
"type": "string"
}
},
"additionalProperties": false,
"required": [
"service"
]
},
"SingleOrVec_ParamSource": {
"x-kubernetes-preserve-unknown-fields": true
},
"SingleOrVec_string": {
"x-kubernetes-preserve-unknown-fields": true
},
"SpannerBranchCopyConfig": {
"description": "Users can choose from the following copy mode to bootstrap their Spanner branch database:\n\n- Empty\n\n Creates an empty database. The instance and database matching the app's configuration are\n created in the emulator, but no schema or data is copied. Useful when the app runs its own\n migrations.\n\n- Schema\n\n Creates the database and copies the DDL (tables and indexes) of the source database.\n\n- All\n\n Copies both schema and data. Use only when the source data volume is minimal.",
"oneOf": [
{
"type": "object",
"properties": {
"mode": {
"type": "string",
"const": "empty"
},
"tables": {
"type": [
"object",
"null"
],
"additionalProperties": {
"$ref": "#/$defs/BranchItemCopyConfig"
}
}
},
"additionalProperties": false,
"required": [
"mode"
]
},
{
"type": "object",
"properties": {
"mode": {
"type": "string",
"const": "schema"
},
"tables": {
"type": [
"object",
"null"
],
"additionalProperties": {
"$ref": "#/$defs/BranchItemCopyConfig"
}
}
},
"additionalProperties": false,
"required": [
"mode"
]
},
{
"type": "object",
"properties": {
"mode": {
"type": "string",
"const": "all"
}
},
"additionalProperties": false,
"required": [
"mode"
]
}
]
},
"SplitQueuesConfig": {
"anyOf": [
{
"type": "object",
"additionalProperties": {
"description": "One queue to split: which broker it is on, which of its messages reach the local application,\nand what happens to those messages. The same struct backs both config shapes: in the list form\nit carries its `queue_id`, in the map form the id is the map key.\n\nAdding a broker is one [`QueueKind`] variant; adding a filter option is one field here, read by\nevery broker through the operator's shared filter code.",
"type": "object",
"properties": {
"filter": {
"description": "The composable filter. See `feature.split_queues.{}.filter`.",
"anyOf": [
{
"$ref": "#/$defs/MessageFilterConfig"
},
{
"type": "null"
}
]
},
"jq_filter": {
"title": "feature.split_queues.{}.jq_filter {#feature-split_queues-queue_id-jq_filter}",
"description": "When this field is specified, for each message, the jq filter runs on a JSON\nrepresentation of the message. If the jq program outputs `true`, that\nmessage is considered as matching the filter. Combined with `filter` or\n`message_filter`, a message must match both.\n\nFor **SQS**, [an SQS `Message` object](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_Message.html)\nis used.\n\nFor **GCP Pub/Sub**, the JSON representation of [`PubsubMessage`](https://cloud.google.com/pubsub/docs/reference/rest/v1/PubsubMessage)\nis used.\n\nFor **Kafka**, an object with `topic`, `partition`, `offset`, `timestamp`, `key`,\n`payload`, and `headers` fields is used. `key`, `payload`, and header values are UTF-8\nstrings, or base64-encoded when not valid UTF-8. With `payload_protobuf` set, the\nobject additionally has a `payload_decoded` field holding the payload decoded from\nprotobuf.\n\nFor **RabbitMQ**, an object with `headers` (the AMQP basic-properties headers table),\n`properties`, and `payload` fields is used. The `payload` and header values are UTF-8\nstrings, or base64-encoded when not valid UTF-8.\n\nFor **Azure Service Bus**, an object with `body`, `application_properties`,\n`message_id`, `content_type`, and `subject` fields is used.\n\nFor **Redis Pub/Sub**, the message payload parsed as JSON is used. Messages whose\npayload is not valid JSON never match.\n\nFor **Temporal**, an object the operator builds for each task is used. Every object has\na `task_type` field, set to either `\"activity\"` or `\"workflow\"`. Activity tasks also\ncarry `workflow_namespace`, `workflow_id`, `run_id`, `workflow_type`, `activity_type`,\n`activity_id`, `attempt`, `header`, and `input` (an array of the decoded arguments).\nWorkflow tasks also carry `workflow_id`, `run_id`, `workflow_type`, `attempt`,\n`task_queue`, `cron_schedule`, `identity`, `first_execution_run_id`, `header`,\n`search_attributes`, `memo`, and `input`.\n\nFor **BullMQ**, the job's `data` field parsed as JSON is used. Jobs whose `data` is not\nvalid JSON never match.\n\nFor **NATS** and **NATSPubSub**, an object with `subject`, `headers`, and `payload`\nfields is used. `payload` is the message body parsed as JSON when the body is JSON, and\na string otherwise (base64-encoded when not valid UTF-8). Unlike `NATS` (JetStream),\ncore NATS pub/sub stores nothing, so delivery to the local application is best-effort:\nmessages published while the split is being set up or torn down are not replayed.\n\nThis can be used to filter messages based on their body content, for example.\n\nThis filter, for example, will tell mirrord to only make available to this local\napplication messages with a json in the message body, with a `customer_email` field\nthat contains \"metalbear.com\": `\".Body | fromjson | .customer_email |\ntest(\\\"metalbear\\\\\\\\.com\\\")\"`",
"type": [
"string",
"null"
]
},
"message_filter": {
"title": "feature.split_queues.{}.message_filter {#feature-split_queues-queue_id-message_filter}",
"description": "The older filter shape: a mapping between message attribute (or header) names and regexes\ntheir values should match. The local application only receives messages that have\n**all** of the named attributes, each matching its pattern. Still supported; new configs\nshould prefer `filter`, which can also match attributes without naming them and compose\nwith `any_of` / `all_of`.\n\nFor Temporal the names are `workflow_id`, `workflow_type`, `activity_type`,\n`header.<name>`, or a search attribute key. For Redis Pub/Sub and BullMQ they are\ntop-level fields of the JSON payload.",
"type": [
"object",
"null"
],
"additionalProperties": {
"type": "string"
}
},
"payload_protobuf": {
"description": "Decodes the raw protobuf payload into a `payload_decoded` field for `jq_filter`, for\nKafka topics that carry plain protobuf instead of JSON. See\n`feature.split_queues.{}.payload_protobuf`.",
"anyOf": [
{
"$ref": "#/$defs/KafkaPayloadProtobuf"
},
{
"type": "null"
}
]
},
"queue_mode": {
"description": "Whether matched messages are stolen from the deployed application or mirrored to it.",
"$ref": "#/$defs/QueueMode"
},
"queue_type": {
"description": "The broker this queue lives on.",
"$ref": "#/$defs/QueueKind"
}
},
"additionalProperties": false,
"required": [
"queue_type"
]
}
},
{
"type": "array",
"items": {
"description": "One queue to split: which broker it is on, which of its messages reach the local application,\nand what happens to those messages. The same struct backs both config shapes: in the list form\nit carries its `queue_id`, in the map form the id is the map key.\n\nAdding a broker is one [`QueueKind`] variant; adding a filter option is one field here, read by\nevery broker through the operator's shared filter code.",
"type": "object",
"properties": {
"filter": {
"description": "The composable filter. See `feature.split_queues.{}.filter`.",
"anyOf": [
{
"$ref": "#/$defs/MessageFilterConfig"
},
{
"type": "null"
}
]
},
"jq_filter": {
"title": "feature.split_queues.{}.jq_filter {#feature-split_queues-queue_id-jq_filter}",
"description": "When this field is specified, for each message, the jq filter runs on a JSON\nrepresentation of the message. If the jq program outputs `true`, that\nmessage is considered as matching the filter. Combined with `filter` or\n`message_filter`, a message must match both.\n\nFor **SQS**, [an SQS `Message` object](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_Message.html)\nis used.\n\nFor **GCP Pub/Sub**, the JSON representation of [`PubsubMessage`](https://cloud.google.com/pubsub/docs/reference/rest/v1/PubsubMessage)\nis used.\n\nFor **Kafka**, an object with `topic`, `partition`, `offset`, `timestamp`, `key`,\n`payload`, and `headers` fields is used. `key`, `payload`, and header values are UTF-8\nstrings, or base64-encoded when not valid UTF-8. With `payload_protobuf` set, the\nobject additionally has a `payload_decoded` field holding the payload decoded from\nprotobuf.\n\nFor **RabbitMQ**, an object with `headers` (the AMQP basic-properties headers table),\n`properties`, and `payload` fields is used. The `payload` and header values are UTF-8\nstrings, or base64-encoded when not valid UTF-8.\n\nFor **Azure Service Bus**, an object with `body`, `application_properties`,\n`message_id`, `content_type`, and `subject` fields is used.\n\nFor **Redis Pub/Sub**, the message payload parsed as JSON is used. Messages whose\npayload is not valid JSON never match.\n\nFor **Temporal**, an object the operator builds for each task is used. Every object has\na `task_type` field, set to either `\"activity\"` or `\"workflow\"`. Activity tasks also\ncarry `workflow_namespace`, `workflow_id`, `run_id`, `workflow_type`, `activity_type`,\n`activity_id`, `attempt`, `header`, and `input` (an array of the decoded arguments).\nWorkflow tasks also carry `workflow_id`, `run_id`, `workflow_type`, `attempt`,\n`task_queue`, `cron_schedule`, `identity`, `first_execution_run_id`, `header`,\n`search_attributes`, `memo`, and `input`.\n\nFor **BullMQ**, the job's `data` field parsed as JSON is used. Jobs whose `data` is not\nvalid JSON never match.\n\nFor **NATS** and **NATSPubSub**, an object with `subject`, `headers`, and `payload`\nfields is used. `payload` is the message body parsed as JSON when the body is JSON, and\na string otherwise (base64-encoded when not valid UTF-8). Unlike `NATS` (JetStream),\ncore NATS pub/sub stores nothing, so delivery to the local application is best-effort:\nmessages published while the split is being set up or torn down are not replayed.\n\nThis can be used to filter messages based on their body content, for example.\n\nThis filter, for example, will tell mirrord to only make available to this local\napplication messages with a json in the message body, with a `customer_email` field\nthat contains \"metalbear.com\": `\".Body | fromjson | .customer_email |\ntest(\\\"metalbear\\\\\\\\.com\\\")\"`",
"type": [
"string",
"null"
]
},
"message_filter": {
"title": "feature.split_queues.{}.message_filter {#feature-split_queues-queue_id-message_filter}",
"description": "The older filter shape: a mapping between message attribute (or header) names and regexes\ntheir values should match. The local application only receives messages that have\n**all** of the named attributes, each matching its pattern. Still supported; new configs\nshould prefer `filter`, which can also match attributes without naming them and compose\nwith `any_of` / `all_of`.\n\nFor Temporal the names are `workflow_id`, `workflow_type`, `activity_type`,\n`header.<name>`, or a search attribute key. For Redis Pub/Sub and BullMQ they are\ntop-level fields of the JSON payload.",
"type": [
"object",
"null"
],
"additionalProperties": {
"type": "string"
}
},
"payload_protobuf": {
"description": "Decodes the raw protobuf payload into a `payload_decoded` field for `jq_filter`, for\nKafka topics that carry plain protobuf instead of JSON. See\n`feature.split_queues.{}.payload_protobuf`.",
"anyOf": [
{
"$ref": "#/$defs/KafkaPayloadProtobuf"
},
{
"type": "null"
}
]
},
"queue_id": {
"title": "feature.split_queues.{}.queue_id {#feature-split_queues-queue_id-queue_id}",
"description": "The id of the queue to split, as it appears in the target's split configuration. List form\nonly: in the map form the id is the key. Does not have to be unique across entries. Use\n`*` to split every queue of the given `queue_type` with this filter.\n\nEmpty only while a map-form entry is being read, before the key is copied in.",
"type": "string"
},
"queue_mode": {
"description": "Whether matched messages are stolen from the deployed application or mirrored to it.",
"$ref": "#/$defs/QueueMode"
},
"queue_type": {
"description": "The broker this queue lives on.",
"$ref": "#/$defs/QueueKind"
}
},
"additionalProperties": false,
"required": [
"queue_type",
"queue_id"
]
}
}
]
},
"SqlBranchMigrationsConfig": {
"description": "<!--${internal}-->\nRuns schema migrations on a SQL branch. Documented on [`DatabaseBranchConfig`].",
"oneOf": [
{
"description": "Apply migrations with [Flyway](https://documentation.red-gate.com/flyway).",
"type": "object",
"properties": {
"flavor": {
"type": "string",
"const": "flyway"
},
"image": {
"description": "Container image override for the migration runner.\n\nRequired with `locations`, which point inside this image.",
"type": [
"string",
"null"
]
},
"locations": {
"description": "Flyway locations inside `image` holding the migration files, for images with the\nSQL baked in (e.g. `filesystem:/flyway/sql`). Mutually exclusive with `path`.",
"type": "array",
"items": {
"type": "string"
}
},
"path": {
"description": "Local directory holding the migration files.\n\nResolved relative to the working directory. Mutually exclusive with `locations`.",
"type": [
"string",
"null"
]
}
},
"additionalProperties": false,
"required": [
"flavor"
]
},
{
"description": "Apply migrations with [Liquibase](https://docs.liquibase.com).",
"type": "object",
"properties": {
"changelog_file": {
"description": "Root changelog file, relative to `path` or to `search_path`.\n\nRecorded in `DATABASECHANGELOG`, so changing it re-runs every changeset.",
"type": "string"
},
"flavor": {
"type": "string",
"const": "liquibase"
},
"image": {
"description": "Container image override for the migration runner.\n\nRequired with `search_path`, which points inside this image.",
"type": [
"string",
"null"
]
},
"path": {
"description": "Local directory holding the changelog files.\n\nResolved relative to the working directory. Mutually exclusive with `search_path`.",
"type": [
"string",
"null"
]
},
"search_path": {
"description": "Liquibase search path inside `image` holding the changelog files\n(e.g. `/liquibase/changelog`). Mutually exclusive with `path`.",
"type": "array",
"items": {
"type": "string"
}
}
},
"additionalProperties": false,
"required": [
"flavor",
"changelog_file"
]
},
{
"description": "Run a user-provided image as the migration job (e.g. an app image whose setup\nscript runs the framework's migration command).",
"type": "object",
"properties": {
"args": {
"description": "Entrypoint args override for the migration container.",
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
},
"command": {
"description": "Entrypoint command override for the migration container.",
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
},
"env": {
"description": "Extra environment variables for the migration container. Values can reference the\ninjected `MIRRORD_DB_*` connection vars with Kubernetes `$(VAR)` expansion.",
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"flavor": {
"type": "string",
"const": "container"
},
"image": {
"description": "Full image reference for the migration container, including the tag.",
"type": "string"
}
},
"additionalProperties": false,
"required": [
"flavor",
"image"
]
}
]
},
"StartupRetryFileConfig": {
"description": "Controls how many times, and how often mirrord retries its initial Kubernetes API requests (e.g.\nfor resolving the target or connecting to the mirrord Operator).\n\nIf you're having cluster connectivity issues when **starting** mirrord, consider increasing\n[`max_retries`](#startup_retry-max_retries) and changing both\n[`min_ms`](#startup_retry-min_ms) and [`max_ms`](#startup_retry-max_ms) to have mirrord retry\nsome of its initial Kubernetes API requests.\n\n```json\n{\n \"startup_retry\": {\n \"min_ms\": 500,\n \"max_ms\": 5000,\n \"max_retries\": 2,\n }\n}\n```",
"type": "object",
"properties": {
"max_ms": {
"title": "startup_retry.max_ms {#startup_retry-max_ms}",
"description": "Sets the max interval (in milliseconds) of retries for Kubernetes API requests made by\nmirrord during startup (e.g. for resolving the target or connecting to the mirrord\nOperator).\n\nDefaults to `5000` milliseconds.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
},
"max_retries": {
"title": "startup_retry.max_retries {#startup_retry-max_retries}",
"description": "Sets the max amount of retries for Kubernetes API requests made by mirrord during startup\n(e.g. for resolving the target or connecting to the mirrord Operator).\n\nIf you want to **disable** request retries, set this value to `0`.\n\nDefaults to `2`.",
"type": [
"integer",
"null"
],
"format": "uint32",
"minimum": 0
},
"min_ms": {
"title": "startup_retry.min_ms {#startup_retry-min_ms}",
"description": "Sets the min interval (in milliseconds) of retries for Kubernetes API requests made by\nmirrord during startup (e.g. for resolving the target or connecting to the mirrord\nOperator).\n\nDefaults to `500` milliseconds.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
}
},
"additionalProperties": false
},
"StatefulSetTarget": {
"type": "object",
"properties": {
"container": {
"type": [
"string",
"null"
]
},
"stateful_set": {
"type": "string"
}
},
"additionalProperties": false,
"required": [
"stateful_set"
]
},
"Target": {
"oneOf": [
{
"$ref": "#/$defs/DeploymentTarget"
},
{
"$ref": "#/$defs/PodTarget"
},
{
"$ref": "#/$defs/RolloutTarget"
},
{
"$ref": "#/$defs/JobTarget"
},
{
"$ref": "#/$defs/CronJobTarget"
},
{
"$ref": "#/$defs/StatefulSetTarget"
},
{
"$ref": "#/$defs/ServiceTarget"
},
{
"$ref": "#/$defs/ReplicaSetTarget"
},
{
"$ref": "#/$defs/LabelTarget"
},
{
"$ref": "#/$defs/ServerlessTarget"
},
{
"enum": [
"targetless"
]
}
]
},
"TargetFileConfig": {
"anyOf": [
{
"anyOf": [
{
"$ref": "#/$defs/Target"
},
{
"type": "null"
},
{
"type": "string"
}
]
},
{
"type": "object",
"properties": {
"namespace": {
"type": [
"string",
"null"
]
},
"path": {
"description": "<!--${internal}-->\nPath is optional so that it can also be specified via env var instead of via conf file,\nbut it is not optional in a resulting [`TargetConfig`] object - either there is a path,\nor the target configuration is `None`.",
"anyOf": [
{
"$ref": "#/$defs/Target"
},
{
"type": "null"
},
{
"type": "string"
}
]
}
},
"additionalProperties": false
}
]
},
"TlsDeliveryProtocol": {
"oneOf": [
{
"description": "TLS traffic will be delivered over TCP.",
"type": "string",
"const": "tcp"
},
{
"description": "TLS traffic will be delivered over TLS.",
"type": "string",
"const": "tls"
}
]
},
"ToggleableConfig": {
"anyOf": [
{
"type": "boolean"
},
{
"$ref": "#/$defs/EnvFileConfig"
}
]
},
"ToggleableConfig2": {
"anyOf": [
{
"type": "boolean"
},
{
"$ref": "#/$defs/FsUserConfig"
}
]
},
"ToggleableConfig3": {
"anyOf": [
{
"type": "boolean"
},
{
"$ref": "#/$defs/NetworkFileConfig"
}
]
},
"ToggleableConfig4": {
"anyOf": [
{
"type": "boolean"
},
{
"$ref": "#/$defs/IncomingFileConfig"
}
]
},
"ToggleableConfig5": {
"anyOf": [
{
"type": "boolean"
},
{
"$ref": "#/$defs/HttpFilterFileConfig"
}
]
},
"ToggleableConfig6": {
"anyOf": [
{
"type": "boolean"
},
{
"$ref": "#/$defs/OutgoingFileConfig"
}
]
},
"ToggleableConfig7": {
"anyOf": [
{
"type": "boolean"
},
{
"$ref": "#/$defs/DnsFileConfig"
}
]
},
"TurbopufferBranchCopyConfig": {
"description": "Users can choose from the following copy modes to bootstrap their turbopuffer branch:\n\n- Empty (default)\n\n Reserves a fresh namespace name with nothing in it; turbopuffer creates the namespace on\n the app's first write.\n\n- All\n\n Branches the source namespace: an instant copy-on-write clone with every document and\n the schema. Writes to either side never reach the other.\n\n```json\n{ \"copy\": { \"mode\": \"all\" } }\n```",
"oneOf": [
{
"type": "object",
"properties": {
"mode": {
"type": "string",
"const": "empty"
}
},
"additionalProperties": false,
"required": [
"mode"
]
},
{
"type": "object",
"properties": {
"mode": {
"type": "string",
"const": "all"
}
},
"additionalProperties": false,
"required": [
"mode"
]
}
]
},
"VecOrSingle": {
"anyOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
]
},
"VecOrSingle2": {
"anyOf": [
{
"type": "integer",
"format": "uint16",
"maximum": 65535,
"minimum": 0
},
{
"type": "array",
"items": {
"type": "integer",
"format": "uint16",
"maximum": 65535,
"minimum": 0
}
}
]
},
"io.k8s.api.core.v1.ResourceClaim": {
"description": "ResourceClaim references one entry in PodSpec.ResourceClaims.",
"type": "object",
"properties": {
"name": {
"description": "Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container.",
"type": "string"
},
"request": {
"description": "Request is the name chosen for a request in the referenced claim. If empty, everything from the claim is made available, otherwise only the result of this request.",
"type": "string"
}
},
"required": [
"name"
]
},
"io.k8s.api.core.v1.ResourceRequirements": {
"description": "ResourceRequirements describes the compute resource requirements.",
"type": "object",
"properties": {
"claims": {
"description": "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container.\n\nThis is an alpha field and requires enabling the DynamicResourceAllocation feature gate.\n\nThis field is immutable. It can only be set for containers.",
"type": "array",
"items": {
"$ref": "#/$defs/io.k8s.api.core.v1.ResourceClaim"
}
},
"limits": {
"description": "Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/",
"type": "object",
"additionalProperties": {
"$ref": "#/$defs/io.k8s.apimachinery.pkg.api.resource.Quantity"
}
},
"requests": {
"description": "Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/",
"type": "object",
"additionalProperties": {
"$ref": "#/$defs/io.k8s.apimachinery.pkg.api.resource.Quantity"
}
}
}
},
"io.k8s.api.core.v1.Toleration": {
"description": "The pod this Toleration is attached to tolerates any taint that matches the triple <key,value,effect> using the matching operator <operator>.",
"type": "object",
"properties": {
"effect": {
"description": "Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute.",
"type": "string"
},
"key": {
"description": "Key is the taint key that the toleration applies to. Empty means match all taint keys. If the key is empty, operator must be Exists; this combination means to match all values and all keys.",
"type": "string"
},
"operator": {
"description": "Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category.",
"type": "string"
},
"tolerationSeconds": {
"description": "TolerationSeconds represents the period of time the toleration (which must be of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, it is not set, which means tolerate the taint forever (do not evict). Zero and negative values will be treated as 0 (evict immediately) by the system.",
"type": "integer",
"format": "int64"
},
"value": {
"description": "Value is the taint value the toleration matches to. If the operator is Exists, the value should be empty, otherwise just a regular string.",
"type": "string"
}
}
},
"io.k8s.apimachinery.pkg.api.resource.Quantity": {
"description": "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n``` <quantity> ::= <signedNumber><suffix>\n\n\t(Note that <suffix> may be empty, from the \"\" case in <decimalSI>.)\n\n<digit> ::= 0 | 1 | ... | 9 <digits> ::= <digit> | <digit><digits> <number> ::= <digits> | <digits>.<digits> | <digits>. | .<digits> <sign> ::= \"+\" | \"-\" <signedNumber> ::= <number> | <sign><number> <suffix> ::= <binarySI> | <decimalExponent> | <decimalSI> <binarySI> ::= Ki | Mi | Gi | Ti | Pi | Ei\n\n\t(International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n\n<decimalSI> ::= m | \"\" | k | M | G | T | P | E\n\n\t(Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n\n<decimalExponent> ::= \"e\" <signedNumber> | \"E\" <signedNumber> ```\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n\n- No precision is lost - No fractional digits will be emitted - The exponent (or suffix) is as large as possible.\n\nThe sign will be omitted unless the number is negative.\n\nExamples:\n\n- 1.5 will be serialized as \"1500m\" - 1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation.",
"x-kubernetes-int-or-string": true
}
}
}