{"$schema":"https://json-schema.org/draft/2020-12/schema","title":"StackSpec","description":"A Cycle stack file is an \"environment as code\". Anything that can be done in an environment on Cycle can be described in a stack file and deployed as a new environment.\nStack files can list multiple containers and their configurations, load balancer settings, scoped variables, and much more.\n","type":"object","required":["version","containers"],"properties":{"version":{"type":"string","description":"The version of the Cycle stack file used.","enum":["1.0"]},"about":{"description":"Various properties describing this stack.","type":["object","null"],"required":["description","version"],"properties":{"version":{"type":"string","description":"A custom, user-defined version of the stack."},"description":{"type":"string","description":"Custom, user-defined details about this stack."}}},"scoped_variables":{"description":"Describes variables that are assigned to one or more containers at runtime. Can be assigned as an environment variable, written as a file inside the container(s), or accessed over the internal API.","type":["array","null"],"items":{"title":"StackSpecScopedVariable","type":"object","required":["identifier","scope","access"],"properties":{"identifier":{"title":"Identifier","type":"string","description":"A human-readable identifier used to refer to a resource, where using the official ID may be inconvenient.\nThe identifier is automatically tokenized from the name/relevant field of the resource if one is not provided. For example, a container named \"My Container\" will\nhave the identifier of `my-container` and is automatically created by the platform.\n\nThe identifier does not have to be unique.\n"},"scope":{"type":"object","required":["containers"],"properties":{"containers":{"type":"object","description":"Describes the containers that have access to this scoped variable.","required":["global"],"properties":{"global":{"type":"boolean","description":"If true, all containers in the environment will have access to this variable."},"ids":{"type":["array","null"],"description":"A list of container IDs that are granted access to this variable.","items":{"type":"string"}},"identifiers":{"type":["array","null"],"description":"A list of container identifiers that are granted access to this variable.","items":{"$ref":"#/properties/scoped_variables/items/properties/identifier"}}}}}},"access":{"type":"object","properties":{"env_variable":{"description":"Grants access to this variable from within a container as an environment variable.","type":["object","null"],"required":["key"],"properties":{"key":{"description":"The environment variable inside the container that stores the value of the variable.","type":"string"}}},"internal_api":{"description":"Grants access to this variable over the Internal API.","type":["object","null"],"properties":{"duration":{"description":"Sets the duration that this variable can be accessed over the Internal API, after container start. Provides additional security as sensitive data can only be accessed for a limited time.","anyOf":[{"title":"Duration","type":"string","description":"A string signifying a duration of time. Valid time units are \"ns\", \"us\" (or \"µs\"), \"ms\", \"s\", \"m\", \"h\", \"d\", \"w\", \"y\".","example":"72h45m2s"},{"type":"null"}]}}},"file":{"description":"Grants access to this variable as a file inside the container.","type":["object","null"],"required":["decode","path"],"x-ogen-properties":{"decode":{"name":"DecodeBase64"}},"properties":{"decode":{"description":"When true, Cycle will interpret this variable as a base-64 encoded string, and decode it before writing it to the file inside the container.","type":"boolean"},"path":{"type":["string","null"],"example":"/var/run/cycle/variables/","description":"The absolute path to write the variable to (including file name). If `null`, it will be written to `/var/run/cycle/variables/{variable-identifier}`."}}}}},"source":{"anyOf":[{"type":"null"},{"type":"object","description":"Describes the source/value of the variable.\n- **raw**: Directly set the value of the variable in the stack. - **url**: Cycle will fetch the variable content from a remote source when the container starts.\n","discriminator":{"propertyName":"type","mapping":{"url":"./StackSpecScopedVariableUrlSource.yml","raw":"./StackSpecScopedVariableRawSource.yml"}},"oneOf":[{"title":"StackSpecScopedVariableUrlSource","type":"object","description":"A variable who's value is fetched from a URL when the container starts.","required":["type","details"],"properties":{"type":{"type":"string","description":"The type of scoped variable.","enum":["url"]},"details":{"type":"object","required":["url","headers","auth_token_url"],"properties":{"url":{"type":"string","description":"The URL to call to fetch the value."},"headers":{"type":"object","description":"Additional headers that can be attached to the URL request. Useful for adding meta-data to third-party services.","additionalProperties":{}},"auth_token_url":{"type":["string","null"],"description":"A URL that can be provided to authenticate with a third party secret service. Cycle will make a request to this URL before fetching the secret URL, and use the response as the value of an Authorization header when requesting the secret."}}}}},{"title":"StackSpecScopedVariableRawSource","type":"object","description":"A variable with a hard-coded value.","required":["type","details"],"properties":{"type":{"type":"string","description":"The type of scoped variable.","enum":["raw"]},"details":{"type":"object","required":["value","blob"],"properties":{"value":{"type":"string","description":"The value of the variable."},"blob":{"type":"boolean","description":"A boolean where true represents the text the user is entering will be multi line."},"secret":{"type":["object","null"],"properties":{"iv":{"type":"string","description":"A string describing the IV Hex associated with the encryption of the variable."},"hint":{"type":"string","description":"A user specified hint that will suggest what the encryption key might be"}}}}}}}]}]}}}},"containers":{"type":"object","description":"A mapping of containers that will be deployed as a part of this stack. The key is used as the container's identifier.","additionalProperties":{"title":"StackContainer","description":"A container template defined within a stack.","type":"object","required":["name","image","config","stateful"],"properties":{"name":{"type":"string","description":"The human-readable name of this container."},"image":{"description":"Details about the image used for this container.","title":"StackSpecContainerImage","type":"object","required":["origin"],"properties":{"name":{"description":"The human-readable name of this image.","type":["string","null"]},"origin":{"description":"Instructions on how to fetch or build this image.","title":"StackImageOrigin","type":"object","discriminator":{"propertyName":"type","mapping":{"docker-hub":"../../images/origins/dockerHub/DockerHubOrigin.yml","docker-file":"../../images/origins/dockerFile/DockerFileOrigin.yml","docker-registry":"../../images/origins/dockerRegistry/DockerRegistryOrigin.yml","oci-registry":"../../images/origins/ociRegistry/OciRegistryOrigin.yml","cycle-source":"../../images/origins/cycleSource/CycleSourceOrigin.yml"}},"oneOf":[{"title":"DockerHubOrigin","type":"object","description":"An image origin where the image is pulled from DockerHub.","required":["type","details"],"properties":{"type":{"type":"string","enum":["docker-hub"]},"details":{"type":"object","required":["target"],"properties":{"existing":{"anyOf":[{"$ref":"#/properties/containers/additionalProperties/properties/image/properties/origin/oneOf/2/properties/details/properties/existing"},{"type":"null"}]},"target":{"type":"string","description":"The DockerHub target string. ex - `mysql:5.7`"},"username":{"type":"string","description":"For authentication, a username."},"token":{"type":"string","description":"For authentication, a token."}}}}},{"title":"DockerFileOrigin","type":"object","description":"An image origin where the image is built from a Dockerfile located in a git repository.","required":["type","details"],"properties":{"type":{"type":"string","enum":["docker-file"]},"details":{"type":"object","properties":{"existing":{"anyOf":[{"$ref":"#/properties/containers/additionalProperties/properties/image/properties/origin/oneOf/2/properties/details/properties/existing"},{"type":"null"}]},"repo":{"anyOf":[{"title":"RepoSourceType","type":"object","description":"Information about the repository.","required":["url"],"properties":{"url":{"type":"string","description":"The URL of the repository."},"branch":{"type":"string","description":"An optional branch arguement. Default value is `master`."},"auth":{"anyOf":[{"type":"null"},{"type":"object","description":"Authentication information for the repository.","discriminator":{"propertyName":"type","mapping":{"http":"../CredentialsHTTP.yml","ssh":"../CredentialsSSH.yml"}},"oneOf":[{"title":"HTTPSourceCredentials","type":"object","required":["type","credentials"],"properties":{"type":{"type":"string","enum":["http"]},"credentials":{"type":"object","description":"Authentication credentails for the Dockerfile image source type when authenticating over HTTP.","required":["username","password"],"properties":{"username":{"type":"string","description":"For authentication, the username."},"password":{"type":"string","description":"For authentication, the password."}}}}},{"title":"SSHSourceCredentials","type":"object","required":["type","credentials"],"properties":{"type":{"type":"string","enum":["ssh"]},"credentials":{"type":"object","description":"Authentication credentials for the Dockerfile image source type when authenticating with SSH.","required":["username","passphrase","private_key"],"properties":{"username":{"type":"string","description":"The username for the repo service, that is used to authenticate an ssh key."},"passphrase":{"type":"string","description":"The passphrase used for the key."},"private_key":{"type":"string","description":"A pem encoded private key."}}}}}]}]},"ref":{"type":["object","null"],"description":"Repository reference information.","required":["type","value"],"properties":{"type":{"type":"string","description":"The type of reference being used."},"value":{"type":"string","description":"The value for the given reference type."}}}}},{"type":"null"}]},"targz_url":{"type":["string","null"],"description":"An endpoint that serves the tar file."},"context_dir":{"type":["string","null"],"description":"The path to the directory to use as the context when building the image."},"build_file":{"type":["string","null"],"description":"The path to the Dockerfile to be used for buiding the image."},"credentials":{"anyOf":[{"title":"DockerfileCredentails","description":"An array of credentials objects to be used when authenticating against private images used by the Dockerfile.","type":"array","items":{"type":"object","description":"Credentials object used for authentication of indirect resources such as private parent images.","properties":{"url":{"type":"string","description":"The url the resource is located at."},"username":{"type":"string","description":"A username for authentication."},"token":{"type":"string","description":"A token for authentication."}}}},{"type":"null"}]}}}}},{"title":"DockerRegistryOrigin","type":"object","description":"An image origin where the image is pulled from a private Docker registry.","required":["type","details"],"properties":{"type":{"type":"string","enum":["docker-registry"]},"details":{"type":"object","required":["target","url"],"properties":{"existing":{"title":"ExistingSource","type":"object","description":"In a stack, specifies an image source ID from which Cycle will derive any values not specified in the stack file. This is useful for avoiding direct placement of credentials in a stack file, for example.","properties":{"source_id":{"$ref":"#/properties/containers/additionalProperties/properties/image/properties/origin/oneOf/4/properties/details/properties/source_id","description":"The ID of the image source this image should be built from."}}},"target":{"type":"string","description":"The image name on the registry."},"url":{"type":"string","description":"The url of the remote registry."},"username":{"type":"string","description":"For authentication, a username."},"token":{"type":"string","description":"For authentication, a token."},"password":{"type":"string","description":"For authentication, a password."}}}}},{"title":"OciRegistryOrigin","type":"object","description":"An image origin that pulls images fro an OCI-compatible registry. Also used for provider-native registries, such as AWS ECR.","required":["type","details"],"properties":{"type":{"type":"string","enum":["oci-registry"]},"details":{"type":"object","required":["target","url"],"properties":{"existing":{"$ref":"#/properties/containers/additionalProperties/properties/image/properties/origin/oneOf/2/properties/details/properties/existing"},"target":{"type":"string","description":"The image name on the registry."},"url":{"type":"string","description":"The url of the remote registry."},"auth":{"title":"RegistryAuth","type":"object","description":"Authentication details for a third party image registry/source.","discriminator":{"propertyName":"type","mapping":{"user":"RegistryAuthUser.yml","provider":"RegistryAuthProvider.yml","webhook":"RegistryAuthWebhook.yml"}},"oneOf":[{"title":"RegistryAuthUser","description":"User/token based credentials for authentication to a third-party image source.","type":"object","required":["type","details"],"properties":{"type":{"type":"string","enum":["user"]},"details":{"type":"object","properties":{"username":{"type":"string"},"token":{"type":"string"}}}}},{"title":"RegistryAuthProvider","description":"Credentials for authentication to a provider-native image registry, such as AWS ECR.","type":"object","required":["type","details"],"properties":{"type":{"type":"string","enum":["provider"]},"details":{"type":"object","required":["flavor","credentials"],"properties":{"flavor":{"type":"string","enum":["ecr"]},"credentials":{"title":"RegistryAuthProviderCredentials","type":"object","properties":{"region":{"type":"string"},"namespace":{"type":"string"},"api_key":{"type":"string"},"secret":{"type":"string"},"subscription_id":{"type":"string"},"client_id":{"type":"string"},"config":{"type":"string","description":"A base64'd string of additional configuration options."}}}}}}},{"title":"RegistryAuthWebhook","description":"Webhook-based authentication to the provided URL. This webhook expects to receive a base-64 string that when decoded is in the format `username:password`","type":"object","required":["type","details"],"properties":{"type":{"type":"string","enum":["webhook"]},"details":{"type":"object","required":["url"],"properties":{"url":{"type":"string"}}}}},{"type":"null"}]}}}}},{"title":"CycleSourceOrigin","type":"object","description":"An image origin that references an image source on Cycle. \n\nThis origin will never be embedded in an image source. It is for use in stacks, describing an image which is already a part of an image source on Cycle.\n","required":["type","details"],"properties":{"type":{"type":"string","enum":["cycle-source"]},"details":{"type":"object","required":["source_id"],"properties":{"source_id":{"description":"The ID referencing the image source where this image originated.","title":"ID","type":"string","format":"objectid","example":"651586fca6078e98982dbd90"}}}}}]},"build":{"description":"Additional details applied when building an image.","type":["object","null"],"required":["args"],"properties":{"args":{"type":"object","description":"A map of build arguments applied to the image at build time.","additionalProperties":{"type":"string"}}}},"builder":{"description":"A specific builder to use. By default, Cycle uses its factory service and a standard build command to build images, but this can be enhanced by using an image builder integration.","type":["object","null"],"required":["integration_id"],"properties":{"integration_id":{"description":"The ID of the integration to use when building the image. The integration must support image building to be compatible.","$ref":"#/properties/services/properties/loadbalancer/anyOf/1/properties/config/anyOf/0/oneOf/1/properties/details/properties/controllers/items/properties/transport/properties/routers/items/properties/match/properties/containers/properties/include/items"}}}}},"annotations":{"type":["object","null"],"description":"Additional user-provided meta data about the container.","additionalProperties":{}},"stateful":{"type":"boolean","description":"Whether or not to mark the container as stateful when deployed. Stateful containers can utilize volumes (stateful data) and are generally used for running databases or other data management applications."},"config":{"type":"object","description":"Configuration options for this container that will be applied when deployed as part of the stack.","title":"StackContainerConfig","required":["network","deploy"],"properties":{"network":{"title":"StackContainerConfigNetwork","description":"Stack configuration options related to the container's network.","type":"object","required":["public","hostname"],"properties":{"public":{"description":"The level of public network access this container should have.","type":"string","enum":["enable","disable","egress-only"]},"hostname":{"description":"The hostname of the container. This is how it can be referenced by other containers in the same environment.","type":"string"},"ports":{"description":"A list of port mappings on this container.","type":"array","items":{"type":"string"},"examples":["80:80","443:80","3000"]}}},"deploy":{"title":"StackContainerConfigDeploy","type":"object","description":"Stack configuration options related to how the container behaves over its lifecycle (startup, shutdown, health checks, etc).","required":["instances"],"properties":{"instances":{"description":"The number of desired instances to deploy.","type":"integer"},"strategy":{"description":"The strategy Cycle will apply when deploying instances of this container.\n- ** resource-density **: Cycle will distribute instances across servers to maintain balanced resource usage. - ** high-availability **: Cycle will deploy instances over servers with an emphasis on geographic and physical separation - ** first-available **: Cycle will deploy one instance to every node that matches the specified criteria. (default) - ** node **: Cycle will deploy one instance to every node that matches the specified criteria. - ** edge **: Cycle will prioritize geographic distribution of instances. - ** function **: Every ingress request/connection receives its own instance. - ** manual **: Cycle will not make any decisions on where instances are deployed. Instead, instances must be deployed manually using the portal or API.\n","type":["string","null"],"enum":["resource-density","manual","high-availability","first-available","node","edge","function"],"default":"first-available"},"function":{"description":"Configuration options for containers using the 'function' deployment strategy.","type":["object","null"],"properties":{"max_pool_size":{"description":"The maximum number of instances that Cycle can pre-allocate (includes auto-scaled instances).","type":["integer","null"]},"max_shard_concurrency":{"description":"For each shard (scheduler), the maximum number of tasks it can run in parallel.","type":["integer","null"]},"max_runtime":{"description":"The maximum amount of time a function instance can run before timing out.","anyOf":[{"$ref":"#/properties/scoped_variables/items/properties/access/properties/internal_api/properties/duration/anyOf/0"},{"type":"null"}]},"max_queue_time":{"description":"The maximum amount of time Cycle will wait for an instance to be available.","anyOf":[{"$ref":"#/properties/scoped_variables/items/properties/access/properties/internal_api/properties/duration/anyOf/0"},{"type":"null"}]}}},"stateful":{"description":"Configuration options for stateful containers.","type":["object","null"],"required":["options"],"properties":{"options":{"description":"Stateful container options.","type":["object","null"],"properties":{"use_base_hostname":{"description":"When enabled, instances will utilize stateless base hostnames instead of being prefixed with a unique ID.","type":["boolean","null"]}}}}},"constraints":{"description":"Configuration options that provide the ability to set restrictions on which nodes instances of this container are able to be deployed to. (i.e. if you have a GPU container, it should only go on nodes with a GPU).","type":["object","null"],"properties":{"node":{"type":["object","null"],"required":["tags"],"properties":{"tags":{"description":"Tags applied to a node. Cycle generates some automatically, but additional, custom tags can be applied on a per-node basis.","type":"object","properties":{"any":{"description":"If a node has at least one of these tags, it is considered a valid deployment target for this container.","type":"array","items":{"type":"string"}},"all":{"description":"A node must have **ALL** of these tags to be considered a valid deployment target for this container.","type":"array","items":{"type":"string"}}}}}}}},"shutdown":{"description":"Configuration options for how this container behaves during shutdown.","type":["object","null"],"properties":{"graceful_timeout":{"description":"How long the platform will wait for a container to stop gracefully.","anyOf":[{"$ref":"#/properties/scoped_variables/items/properties/access/properties/internal_api/properties/duration/anyOf/0"},{"type":"null"}]},"signals":{"type":"array","description":"Signals that should be sent to the container on shutdown.","items":{"type":"string","enum":["SIGTERM","SIGINT","SIGUSR1","SIGUSR2","SIGHUB","SIGKILL","SIGQUIT"]}}}},"startup":{"description":"Configuration options for how this container behaves during startup.","type":["object","null"],"properties":{"delay":{"description":"How long the platform will wait before sending the start signal to the given container.","anyOf":[{"$ref":"#/properties/scoped_variables/items/properties/access/properties/internal_api/properties/duration/anyOf/0"},{"type":"null"}]}}},"update":{"type":["object","null"],"description":"Configurations for how the container behaves during updates.","properties":{"stagger":{"anyOf":[{"$ref":"#/properties/scoped_variables/items/properties/access/properties/internal_api/properties/duration/anyOf/0"},{"type":"null"}],"description":"When set, Cycle will pick a random time from `0 - this duration`, and stagger the instances so they all start at different times (up to the time specified here)."}}},"restart":{"description":"Configuration options for how Cycle should handle restarting this container (i.e. in case the process inside the container dies).","type":["object","null"],"required":["condition","delay","max_attempts"],"properties":{"condition":{"description":"Under what circumstances Cycle should try to restart this container.","type":"string","enum":["always","never","failure"]},"delay":{"$ref":"#/properties/scoped_variables/items/properties/access/properties/internal_api/properties/duration/anyOf/0","description":"How long the platform will wait between restart attempts."},"max_attempts":{"type":"integer","description":"The maximum number of restart attempts Cycle will make."}}},"health_check":{"description":"Configuration options for automated container health checks.","type":["object","null"],"required":["command","retries","interval","timeout","restart"],"properties":{"command":{"type":"string","description":"The command or script to run to verify the health of the container. This script is run inside the container by Cycle.\nThis command accepts two types of entries:\n- The first is a reference to a script that already lives in the container filesystem. This can be defined by giving the full path to the script as the value. - The second format is an inline script. If you need the code to execute within a shell, wrap the commands in escaped quotes like this `\"\\\"curl -s -o /dev/console -w \\\"%{http_code}\\\" http://localhost:3000/_health | grep '200' && exit 0 || exit 1\\\"\"`. Do not use the `/bin/sh -c ` format, this will not be accepted.\n","example":"/bin/sh healthcheck.sh"},"retries":{"type":"integer","description":"The number of times to retry the command before marking an instance unhealthy."},"interval":{"$ref":"#/properties/scoped_variables/items/properties/access/properties/internal_api/properties/duration/anyOf/0","description":"How long to wait between running health checks."},"timeout":{"$ref":"#/properties/scoped_variables/items/properties/access/properties/internal_api/properties/duration/anyOf/0","description":"How long before a health check attempt times out."},"restart":{"type":"boolean","description":"A boolean where true represents the desire for the container to restart if any instance is unhealthy."},"delay":{"anyOf":[{"$ref":"#/properties/scoped_variables/items/properties/access/properties/internal_api/properties/duration/anyOf/0"},{"type":"null"}],"description":"How long to wait after a container start event before running health checks."}}},"telemetry":{"description":"Configuration options for how the instance telemetry (CPU usage, etc) is handled.","type":["object","null"],"required":["disable"],"properties":{"retention":{"anyOf":[{"$ref":"#/properties/scoped_variables/items/properties/access/properties/internal_api/properties/duration/anyOf/0"},{"type":"null"}],"description":"How long telemetry data should be retained."},"interval":{"anyOf":[{"$ref":"#/properties/scoped_variables/items/properties/access/properties/internal_api/properties/duration/anyOf/0"},{"type":"null"}],"description":"The duration between samples."},"webhook":{"type":["string","null"],"description":"A URL where Cycle will send telemetry data to. The payload will be an instance resource snapshot."},"disable":{"type":"boolean","description":"If true, Cycle will not aggregate telemetry for this container's instances."}}}}},"scaling":{"description":"Configuration options for auto-scaling.","anyOf":[{"title":"StackContainerConfigScaling","type":"object","description":"Stack configuration options for auto-scaling.","required":["autoscale_group","instances","window","thresholds"],"properties":{"autoscale_group":{"anyOf":[{"$ref":"#/properties/scoped_variables/items/properties/identifier"},{"type":"null"}],"description":"The identifier of the auto-scaling group assigned to this container. The auto-scale group determines which infrastructure this container can spin up if it needs more resources to meet demand. Setting it to `null` will limit auto-scaling to only instances."},"instances":{"type":"object","description":"Describes the criteria for deploying new instances when an auto-scale criteria is met.","required":["max","max_server","min_ttl"],"properties":{"max":{"type":"integer","description":"Maximum additional instances the auto-scaler will run at any time."},"max_server":{"type":"integer","description":"Minimum number of instances per server."},"min_ttl":{"$ref":"#/properties/scoped_variables/items/properties/access/properties/internal_api/properties/duration/anyOf/0","description":"Minimum amount of time an instance will live."}}},"window":{"description":"Duration in which the auto-scaler will watch for changes.","$ref":"#/properties/scoped_variables/items/properties/access/properties/internal_api/properties/duration/anyOf/0"},"thresholds":{"description":"An array of rules that dictate when a scaling event will be triggered.","type":"array","items":{"title":"StackContainerScaleThreshold","type":"object","description":"Discriminated union describing the different types of scaling threshold and their respective details","discriminator":{"propertyName":"type","mapping":{"ram":"./StackContainerScaleThresholdRam.yml","cpu":"./StackContainerScaleThresholdCpu.yml","custom":"./StackContainerScaleThresholdCustom.yml","network-connections":"./StackContainerScaleThresholdNetworkConnections.yml","network-requests":"./StackContainerScaleThresholdNetworkRequests.yml","network-throughput":"./StackContainerScaleThresholdNetworkThroughput.yml"}},"oneOf":[{"title":"StackContainerScaleThresholdRam","type":"object","description":"Describes the RAM threshold at which scaling will occur.","required":["type","details"],"properties":{"type":{"type":"string","enum":["ram"]},"details":{"type":"object","required":["usage"],"properties":{"usage":{"type":"string","description":"The target average RAM usage of all instances of this container. Going above this threshold will trigger a scaling event. This threshold must be greater than 25MB.","examples":["2G","25MB"]}}}}},{"title":"StackContainerScaleThresholdCpu","type":"object","description":"Describes the CPU threshold at which scaling will occur.","required":["type","details"],"properties":{"type":{"type":"string","enum":["cpu"]},"details":{"type":"object","required":["utilization"],"properties":{"utilization":{"type":"integer"}}}}},{"title":"StackContainerScaleThresholdNetworkConnections","type":"object","description":"Describes the network connections threshold at which scaling will occur.","required":["type","details"],"properties":{"type":{"type":"string","enum":["network-connections"]},"details":{"type":"object","required":["connections_total"],"properties":{"connections_total":{"type":"integer"}}}}},{"title":"StackContainerScaleThresholdNetworkRequests","type":"object","description":"Describes the network requests threshold at which scaling will occur.","required":["type","details"],"properties":{"type":{"type":"string","enum":["network-requests"]},"details":{"type":"object","required":["requests_total"],"properties":{"requests_total":{"type":"integer"}}}}},{"title":"StackContainerScaleThresholdNetworkThroughput","type":"object","description":"Describes the network throughput threshold at which scaling will occur.","required":["type","details"],"properties":{"type":{"type":"string","enum":["network-throughput"]},"details":{"type":"object","required":["private","bandwidth"],"properties":{"private":{"type":"boolean"},"bandwidth":{"type":"string","description":"The limit (maximum) amount of throughput each instance of the given container can use before triggering a scaling event.","examples":["1G","50M"]}}}}}]}}}},{"type":"null"}]},"runtime":{"anyOf":[{"title":"StackContainerConfigRuntime","description":"Configuration options related to how the container behaves while it is running (environment variables, command overrides, kernel capabilities, etc. )","type":"object","properties":{"workdir":{"type":"string","description":"The working directory to execute the command in."},"command":{"type":"object","description":"The command to execute when this container starts. Will override the default specified in the container.","properties":{"path":{"type":"string"},"args":{"type":"string"}}},"environment_vars":{"type":"object","description":"A map of environment variables that will be injected into the container.","additionalProperties":{"type":"string"}},"namespaces":{"type":"array","description":"Container namespaces to apply. By default, all are applied. Removing/changing this can have security implications.","items":{"type":"string","enum":["ipc","pid","uts","network","mount","user","cgroup"]}},"sysctl":{"type":"object","description":"Sysctl options to apply.","additionalProperties":{"type":"string"}},"rlimits":{"type":"object","description":"RLIMIT options to apply.","additionalProperties":{"type":"object","required":["hard","soft"],"properties":{"hard":{"type":"integer"},"soft":{"type":"integer"}}}},"seccomp":{"type":"object","description":"Configuration options for seccomp. Cycle enables seccomp by default.","required":["disable","rules"],"properties":{"disable":{"type":"boolean"},"rules":{"type":"array","items":{"type":"object","required":["capabilities","syscall"],"properties":{"capabilities":{"type":"object","required":["includes","excludes"],"properties":{"includes":{"type":"string"},"excludes":{"type":"string"}}},"syscall":{"type":"object","required":["names","action"],"properties":{"names":{"type":"array","items":{"type":"string"}},"action":{"type":"string","enum":["SCMP_ACT_KILL","SCMP_ACT_KILL_PROCESS","SCMP_ACT_KILL_THREAD","SCMP_ACT_TRAP","SCMP_ACT_ERRNO","SCMP_ACT_TRACE","SCMP_ACT_ALLOW","SCMP_ACT_LOG","SCMP_ACT_NOTIFY"]},"errnoRet":{"type":"integer"},"args":{"type":"array","items":{"type":"object","required":["index","value","op"],"properties":{"index":{"type":"integer"},"value":{"type":"integer"},"valuetwo":{"type":"integer"},"op":{"type":"string","enum":["SCMP_CMP_NE","SCMP_CMP_LT","SCMP_CMP_LE","SCMP_CMP_EQ","SCMP_CMP_GE","SCMP_CMP_GT","SCMP_CMP_MASKED_EQ"]}}}}}}}}}}},"host":{"description":"Configuration options regarding the underlying host.","type":["object","null"],"properties":{"expose_proc":{"description":"If true, Cycle will mount the `/proc` directory into the container, giving it access to the host metrics. This is useful if you're running i.e. a monitoring agent.","type":["boolean","null"]}}},"privileged":{"description":"If true, the container process will run in fully-privileged mode. **WARNING** This is considered insecure, and should only be done if you know what you're doing.","type":"boolean"},"capabilities":{"type":"array","description":"Additional Linux kernel capabilities to apply to this container process.","items":{"type":"string","enum":["CAP_CHOWN","CAP_FSETID","CAP_DAC_OVERRIDE","CAP_FOWNER","CAP_SETFCAP","CAP_SETGID","CAP_SETUID","CAP_KILL","CAP_MKNOD","CAP_NET_BIND_SERVICE","CAP_NET_RAW","CAP_AUDIT_WRITE","CAP_SYS_CHROOT","CAP_SETPCAP","CAP_DAC_READ_SEARCH","CAP_NET_ADMIN","CAP_NET_BROADCAST","CAP_SYS_ADMIN","CAP_SYS_MODULE","CAP_SYS_NICE","CAP_SYS_PACCT","CAP_SYS_PTRACE","CAP_SYS_RAWIO","CAP_SYS_RESOURCE","CAP_SYS_BOOT","CAP_SYS_TIME","CAP_SYS_TTY_CONFIG","CAP_SYSLOG","CAP_AUDIT_CONTROL","CAP_AUDIT_READ","CAP_IPC_LOCK","CAP_IPC_OWNER","CAP_LINUX_IMMUTABLE","CAP_MAC_ADMIN","CAP_MAC_OVERRIDE","CAP_BLOCK_SUSPEND","CAP_LEASE","CAP_WAKE_ALARM"]}},"rootfs":{"type":"object","description":"Configuration options for the root filesystem.","required":["readonly"],"properties":{"readonly":{"description":"If true, the container's filesystem will be read-only.","type":"boolean"}}}}},{"type":"null"}]},"resources":{"anyOf":[{"title":"StackContainerConfigResources","description":"Configuration options for container resource limits and reserves.","type":"object","required":["cpu","ram"],"properties":{"cpu":{"type":"object","properties":{"shares":{"type":"object","required":["limit","reserve"],"properties":{"limit":{"type":"integer"},"reserve":{"type":"integer"}}},"cpus":{"type":"string"}}},"ram":{"type":"object","properties":{"limit":{"type":"string"},"reserve":{"type":"string"},"swappiness":{"type":"number"}}}}},{"type":"null"}]},"integrations":{"anyOf":[{"title":"StackContainerConfigIntegrations","type":"object","description":"Configuration options for additional integrations/features that Cycle provides.","properties":{"webhooks":{"description":"Enable additional webhooks that Cycle will call out to during the course of a container's lifetime. All webhooks send a payload as an object containing the instance, container, server, and environment IDs.","type":["object","null"],"properties":{"events":{"description":"Webhooks that are triggered during a container event.","type":["object","null"],"properties":{"deploy":{"description":"Cycle will call this endpoint when the container is deployed.","type":["string","null"]},"start":{"description":"Cycle will call this endpoint when the container is started.","type":["string","null"]},"stop":{"description":"Cycle will call this endpoint when the container is stopped.","type":["string","null"]}}},"config":{"description":"The webhook to hit when the container's configuration is changed.","type":["string","null"]}}},"lets_encrypt":{"description":"When enabled, this integration will configure Let's Encrypt certificates that will be injected into the container at runtime. The certificates will be managed by the platform and renewed automatically.","type":["object","null"],"required":["enable"],"properties":{"enable":{"type":"boolean"},"certificate_path":{"type":["string","null"]},"chain_path":{"type":["string","null"]},"key_path":{"type":["string","null"]},"bundle_path":{"type":["string","null"]},"additional_certs_path":{"type":["string","null"]}}},"files":{"description":"When enabled, Cycle will fetch and inject remote files into the container at the specified destination during runtime.","type":["array","null"],"items":{"type":"object","required":["source","destination"],"properties":{"source":{"type":"string"},"destination":{"type":"string"}}}},"backups":{"description":"When enabled, Cycle will automatically manage backups of this container. This is only available for stateful containers.","type":["object","null"],"required":["destination","backup","restore","retention"],"properties":{"integration_id":{"$ref":"#/properties/services/properties/loadbalancer/anyOf/1/properties/config/anyOf/0/oneOf/1/properties/details/properties/controllers/items/properties/transport/properties/routers/items/properties/match/properties/containers/properties/include/items","description":"An identifier of an integration that supports backups. All backups will be sent to this destination."},"backup":{"type":"object","description":"Configuration options for how the container should be backed up.","required":["command","timeout","cron_string"],"properties":{"command":{"description":"The command to run to capture a backup. The output sent to `STDOUT` will be captured and sent to the specified integration.","type":"string"},"timeout":{"description":"How long the backup will attempt to run before timing out.","anyOf":[{"$ref":"#/properties/scoped_variables/items/properties/access/properties/internal_api/properties/duration/anyOf/0"},{"type":"null"}]},"cron_string":{"description":"A cron string describing how often to run the backup command.","type":["string","null"]}}},"restore":{"description":"Configuration options for how the backup should be restored.","type":["object","null"],"required":["command","timeout"],"properties":{"command":{"type":"string"},"timeout":{"description":"The time in seconds for the restore to attempt to complete before timing out.","anyOf":[{"$ref":"#/properties/scoped_variables/items/properties/access/properties/internal_api/properties/duration/anyOf/0"},{"type":"null"}]}}},"retention":{"description":"How long the platform will keep backups. Default is 1 year.","anyOf":[{"$ref":"#/properties/scoped_variables/items/properties/access/properties/internal_api/properties/duration/anyOf/0"},{"type":"null"}],"default":"365d"}}},"shared_file_systems":{"description":"When enabled, Cycle will mount a shared host directory into this container. The directory will be shared with all other containers that mount it.","type":["object","null"],"additionalProperties":{"type":"object","required":["writable","mount_point"],"properties":{"writable":{"type":"boolean"},"mount_point":{"type":"string"}}}},"logs":{"description":"When enabled, allows more customization to be applied to logging for the container.","type":["object","null"],"properties":{"groups":{"description":"A tag used for applying log filters and analytics.","type":["array","null"],"items":{"$ref":"#/properties/scoped_variables/items/properties/identifier"}}}}}},{"type":"null"}]}}},"role":{"description":"The role applied to this container. **Not yet implemented**","type":["string","null"],"enum":["conductor"]},"volumes":{"description":"A list of configurations for volumes that will be attached to the container. Only applicable if the container is set to `stateful`.","type":["array","null"],"items":{"title":"StackContainerVolume","description":"A container volume configuration.","type":"object","required":["destination","read_only"],"properties":{"local":{"description":"Configuration options for local volumes.","type":"object","required":["max_size"],"properties":{"max_size":{"description":"The maximum size this volume can grow to. Container volumes on Cycle are thinly provisioned, meaning this isn't an allocation - the volume will only use the space it needs up to this size.","type":"string","examples":["5G","500M"]},"storage_pool":{"description":"A boolean where true signifies using the largest drive over 2TB for the target server.","type":"boolean"}}},"destination":{"description":"The path this volume should be mounted at inside the container.","type":"string"},"read_only":{"description":"If true, the container will be unable to write data to the volume.","type":"boolean"},"remote_access":{"description":"Configuration options for setting up remote access to this volume via SFTP.","type":"object","required":["enable","password"],"properties":{"enable":{"description":"If true, this volume will be accessible over SFTP.","type":"boolean"},"ips":{"description":"A list of IPs that SFTP access will be limited to.","type":"array","items":{"type":"string"}},"webhook":{"description":"If set, Cycle will call out to this URL for authentication. Anything other than a 200 response will be considered a validation failure.","type":"string"},"password":{"type":"object","description":"The password used for logging in to this volume via SFTP.","required":["algorithm","data"],"properties":{"algorithm":{"type":"string","description":"The algorithm the password is encoded with. `raw` means the password is plain-text.","enum":["raw","sha512","md5"]},"data":{"description":"The password string.","type":"string"}}}}}}}},"deprecate":{"description":"If true, the container is marked as `deprecated`, and cannot be started anymore. Deprecated containers also don't count toward resource utilization.","type":"boolean"},"lock":{"description":"If true, the container is marked as `locked` and cannot be deleted in any way until the lock is lifted.","type":"boolean"}}}},"services":{"description":"Settings for any auxillary services deployed as part of the environment, such as load balancer and discovery services.","title":"StackSpecServices","type":["object","null"],"properties":{"discovery":{"anyOf":[{"type":"null"},{"title":"StackSpecDiscoveryService","description":"Configuration options for the discovery service.","type":"object","additionalProperties":false,"properties":{"service":{"anyOf":[{"$ref":"#/properties/services/properties/vpn/anyOf/1/properties/service/anyOf/0"},{"type":"null"}]},"config":{"anyOf":[{"title":"StackSpecDiscoveryConfig","type":"object","properties":{"empty_set_delay":{"anyOf":[{"$ref":"#/properties/scoped_variables/items/properties/access/properties/internal_api/properties/duration/anyOf/0"},{"type":"null"}]},"hosts":{"description":"A custom mapping of hosts - for forced resolution of specific IPs for a domain.","type":["object","null"],"additionalProperties":{"type":"object","properties":{"ipv4":{"type":["array","null"],"items":{"type":"string"}},"ipv6":{"type":["array","null"],"items":{"type":"string"}}}}}}},{"type":"null"}]}}}]},"loadbalancer":{"anyOf":[{"type":"null"},{"title":"StackSpecLoadBalancerService","description":"Configuration options for the load balancer service.","type":"object","additionalProperties":false,"properties":{"service":{"anyOf":[{"$ref":"#/properties/services/properties/vpn/anyOf/1/properties/service/anyOf/0"},{"type":"null"}]},"config":{"anyOf":[{"title":"StackSpecLoadBalancerConfig","type":"object","description":"The config object for the loadbalancer service.","discriminator":{"propertyName":"type","mapping":{"haproxy":"types/haproxy/HaProxyLbType.yml","v1":"types/v1/V1LbType.yml","default":"types/DefaultLbType.yml"}},"oneOf":[{"title":"HaProxyLbType","type":"object","required":["type","details","ipv4","ipv6"],"properties":{"ipv4":{"type":"boolean","description":"Allow / disallow traffic to be routed via IPv4."},"ipv6":{"type":"boolean","description":"Allow / disallow traffic to be routed via IPv6."},"type":{"type":"string","enum":["haproxy"]},"details":{"anyOf":[{"title":"HaProxyConfig","type":"object","description":"Describes settings that are passed to HAProxy within the load balancer.","required":["default","ports"],"properties":{"default":{"description":"Settings that are applied to any port that is not overridden in the following ports section.","$ref":"#/properties/services/properties/loadbalancer/anyOf/1/properties/config/anyOf/0/oneOf/0/properties/details/anyOf/0/properties/ports/additionalProperties"},"ports":{"description":"An object that defines how HAProxy will act on a specific port. The key is a custom port, and the value is the same settings object found under `default` above.","type":"object","additionalProperties":{"title":"HAProxyConfigSet","type":"object","required":["frontend","backend"],"properties":{"frontend":{"type":"object","description":"Settings that describe how incoming traffic to the load balancer is handled.","required":["mode","max_connections","timeouts"],"properties":{"mode":{"type":"string","description":"The type of traffic expected by the load balancer for this port. Can be either: \n - tcp: Traffic is forwarded without any parsing or additional manipulation. \n - http: Traffic is treated as web traffic. If a LINKED record is configured for a container exposing this port, the domain will be parsed and it will be forwarded to the proper container. This allows multiple services to run on port 80 in the same environment.","enum":["tcp","http"]},"max_connections":{"type":["integer","null"],"description":"The number of simultaneous connections that can be processed at a time."},"timeouts":{"type":["object","null"],"description":"Various options for handling timeouts when communicating with the client.","required":["client_secs","client_fin_ms","http_keep_alive_ms","http_request_ms"],"properties":{"client_secs":{"type":["integer","null"],"description":"The number of seconds the load balancer will wait for a response from a client before disconnecting."},"client_fin_ms":{"type":["integer","null"],"description":"The number of milliseconds the load balancer will wait for a client to send it data when one direction is already closed. This is particularly useful to avoid keeping connections in a waiting state for too long when clients do not disconnect cleanly."},"http_keep_alive_ms":{"type":["integer","null"],"description":"The number of milliseconds the load balancer will wait for a new HTTP request to start coming after a response was set. See the [HAProxy Docs](https://cbonte.github.io/haproxy-dconv/1.7/configuration.html#4.2-timeout%20http-request) for more information. (`http` mode only)"},"http_request_ms":{"type":["integer","null"],"description":"The number of milliseconds the load balancer will wait for a complete HTTP request. See the [HAProxy Docs](https://cbonte.github.io/haproxy-dconv/1.7/configuration.html#4.2-timeout%20http-request) for more information. (`http` mode only)"}}}}},"backend":{"type":"object","description":"Settings related to how the load balancer routes connections to container instances.","required":["balance","timeouts"],"properties":{"balance":{"type":"string","description":"How connections are balanced across your container instances. Can be one of the following: \n - `roundrobin`: Each container instance is used in turns. \n - `static-rr`: Each container instance is used in turns, but is faster than Round Robin at the expense of being less dynamic. \n - `leastconn`: Routes traffic to the instance with the least number of active connections. \n - `first`: Routes traffic to the first available instance. \n - `source`: The same client IP always reaches the same container instance as long as no instance goes down or up.","enum":["roundrobin","static-rr","leastconn","first","source"]},"timeouts":{"type":["object","null"],"description":"Various options for handling timeouts when communicating with a container instance behind the load balancer.","required":["server_secs","server_fin_ms","connect_ms","queue_ms","tunnel_secs"],"properties":{"server_secs":{"type":["integer","null"],"description":"The number of seconds the load balancer will wait for a response from the container instance. See the [HAProxy Docs](https://cbonte.github.io/haproxy-dconv/1.7/configuration.html#4.2-timeout%20server) for more information."},"server_fin_ms":{"type":["integer","null"],"description":"The number of milliseconds the load balancer will wait for the server to send data when one direction is already closed. See the [HAProxy Docs](https://cbonte.github.io/haproxy-dconv/1.7/configuration.html#4-timeout%20server-fin) for more information."},"connect_ms":{"type":["integer","null"],"description":"The number of milliseconds the load balancer will wait for a successful connection to a container instance. See the [HAProxy Docs](https://cbonte.github.io/haproxy-dconv/1.7/configuration.html#4-timeout%20connect) for more information."},"queue_ms":{"type":["integer","null"],"description":"The number of milliseconds the load balancer will hold connections in a queue when the maximum number of connections has been reached. See the [HAProxy Docs](https://cbonte.github.io/haproxy-dconv/1.7/configuration.html#4-timeout%20queue) for more information."},"tunnel_secs":{"type":["integer","null"],"description":"The number of milliseconds the load balancer will allow for inactivity on a bidirectional tunnel. See the [HAProxy Docs](https://cbonte.github.io/haproxy-dconv/1.7/configuration.html#4-timeout%20tunnel) for more information."}}}}}}}}}},{"type":"null"}]},"bind_host":{"description":"Binds the load balancer to the host server IP address. \n\n**Pros**: This allows for significantly lower cost (utilizing fewer IPv4 addresses), and enables building out a true edge network with lower latency.\n**Cons**: Only 1 environment is allowed on the host. This is because the load balancer is the only ingress point for an environment, and if it is sharing\nthe same IP as the host, that host can only operate under that environment.\n","type":["boolean","null"]}}},{"title":"V1LbType","type":"object","required":["type","details","ipv4","ipv6"],"properties":{"ipv4":{"type":"boolean","description":"Allow / disallow traffic to be routed via IPv4."},"ipv6":{"type":"boolean","description":"Allow / disallow traffic to be routed via IPv6."},"type":{"type":"string","enum":["v1"]},"details":{"title":"V1LbConfig","type":"object","required":["controllers"],"properties":{"controllers":{"type":"array","description":"A configuration for a specific port.","items":{"title":"V1LbController","type":"object","required":["identifier","port"],"properties":{"identifier":{"type":"string","description":"A human-readable identifier for this controller. It will default to the port, i.e. `port-443`, but can be renamed to anything, such as the service this controller represents."},"port":{"type":"integer","description":"The port inbound trafic is accepted on."},"waf":{"anyOf":[{"$ref":"#/properties/services/properties/loadbalancer/anyOf/1/properties/config/anyOf/0/oneOf/1/properties/details/properties/waf/anyOf/0"},{"type":"null"}]},"transport":{"title":"V1LbControllerTransport","description":"Defines how traffic comes in to the load balancer, and how the load balancer handles it.","type":["object","null"],"required":["mode","config","routers","disable"],"properties":{"disable":{"type":"boolean","description":"When true, this controller is disabled and will not be used."},"mode":{"type":"string","description":"The kind of traffic (http/tcp/udp) that will be sent to the load balancer.","enum":["tcp","udp","http"]},"config":{"type":"object","description":"Defines how the transport for this controller operates.","required":["performance","ingress","timeouts","verbosity"],"properties":{"performance":{"type":"boolean","description":"Enable/disable performance mode. If enabled, some telemetry will be disabled to dedicate full processing to handling requests.\nYou will not see per-request breakdowns or URL logging if performance mode is enabled.\n"},"ingress":{"type":"object","description":"Defines how traffic gets into the load balancer.","properties":{"tls":{"type":["object","null"],"required":["enable"],"properties":{"enable":{"type":"boolean","description":"Enables or disables TLS."}}}}},"timeouts":{"type":"object","description":"Defines settings for various types of timeouts.","required":["idle"],"properties":{"idle":{"description":"The total amount of time a connection can be idle before being killed.","$ref":"#/properties/scoped_variables/items/properties/access/properties/internal_api/properties/duration/anyOf/0"}}},"verbosity":{"type":"string","description":"Verbosity describes the level of logging detail for the controller","enum":["low","normal","high","debug"]},"extension":{"type":"object","description":"Extended configurations for the specified transport mode (http/tcp)","discriminator":{"propertyName":"type","mapping":{"tcp":"transports/TcpTransportConfig.yml","http":"transports/HttpTransportConfig.yml","udp":"transports/UdpTransportConfig.yml"}},"oneOf":[{"title":"TcpTransportConfig","description":"Additional configuration options for the TCP transport mode.","type":"object","required":["type","details"],"properties":{"type":{"type":"string","enum":["tcp"]},"details":{"type":"object","required":["connections","telemetry"],"properties":{"connections":{"type":"object","additionalProperties":{}},"telemetry":{"type":"object","description":"Configuration options for how telemetry is handled on the load balancer.","additionalProperties":{}}}}}},{"title":"HttpTransportConfig","description":"Additional configuration options for the HTTP transport mode.","type":"object","required":["type","details"],"properties":{"type":{"type":"string","enum":["http"]},"details":{"type":"object","required":["connections","telemetry"],"properties":{"connections":{"type":"object","required":["max_idle_conns_per_connection"],"description":"Defines extra configuration options connections to the load balancer","properties":{"max_idle_conns_per_connection":{"type":["integer","null"],"description":"Maximum number of simultaneous connections (via http/2) per connection."}}},"telemetry":{"type":"object","description":"Configuration options for how telemetry is handled.","properties":{"max_trackable_urls":{"description":"Determines how many URLs the load balancer will track at one time. Defaults to 150.","type":["integer","null"]},"track_invalid_requests":{"description":"Whether or not to track invalid requests. An invalid request is a request that came in that no router existed for. Usually this means bot requests. Defaults to false.","type":["boolean","null"]},"ignore_paths":{"description":"An array of paths to exclude from tracking.","type":["array","null"],"items":{"type":"string"}}}}}}}},{"title":"UdpTransportConfig","description":"Additional configuration options for the UDP transport mode.","type":"object","required":["type","details"],"properties":{"type":{"type":"string","enum":["udp"]},"details":{"type":"object","required":["telemetry"],"properties":{"telemetry":{"type":"object","description":"Configuration options for how telemetry is handled on the load balancer.","additionalProperties":{}}}}}}]}}},"routers":{"type":"array","description":"Defines where traffic is sent. Many can be defined per controller.","items":{"title":"V1LbRouterConfig","description":"A specific router configuration that describes how traffic matching the rule is handled.","type":"object","required":["match","mode","config"],"properties":{"match":{"type":"object","description":"The ruleset for this router to be selected. If both `domains`` and `internal_port` are null, then this match acts as a wildcard and will match all.","required":["domains","internal_ports"],"properties":{"domains":{"type":["array","null"],"description":"The specific domains to match against.","items":{"type":"string"}},"internal_ports":{"type":["array","null"],"description":"The specific ports to match against.","items":{"type":"integer"}},"path":{"type":["string","null"]},"containers":{"type":["object","null"],"description":"Match traffic destined (or not destined) for a particular container.","properties":{"include":{"description":"Match any traffic that would be routed to one of these containers.","type":["array","null"],"items":{"title":"HybridIdentifier","type":"string","description":"Either a resource ID (objectid - i.e. 651586fca6078e98982dbd90) or a resource Identifier (human-readable)","example":"my-image-source"}},"exclude":{"description":"Match any traffic that would NOT be routed to one of these containers.","type":["array","null"],"items":{"$ref":"#/properties/services/properties/loadbalancer/anyOf/1/properties/config/anyOf/0/oneOf/1/properties/details/properties/controllers/items/properties/transport/properties/routers/items/properties/match/properties/containers/properties/include/items"}}}}}},"mode":{"type":"string","description":"How to route the traffic to the destination.\n`random`: Pick a valid destination at random.\n`round-robin`: Send each request to the 'next' destination on the list, restarting from the beginning when the last destination is used.\n","enum":["random","round-robin"]},"config":{"type":"object","required":["sticky_sessions","destination_retries","timeouts"],"properties":{"sticky_sessions":{"type":"boolean","description":"If a request comes in from the same origin, ensure it hits the same destination."},"destination_retries":{"type":"integer","description":"If a destination is unavailable, retry up to [x] times, instead of immediately failing with a 503/504 error."},"destination_prioritization":{"default":null,"oneOf":[{"type":"string","description":"Hints to the load balancer how to prioritize traffic to instances.\n**random**: Chooses a random instance. **latency**: Prioritizes lower latency instances.\n","enum":["latency","random"]},{"type":"null"}]},"tls":{"type":["object","null"],"description":"TLS termination configuration. If null, the platform will use the default configuration. Port 443 by default has TLS termination enabled.","properties":{"server_name":{"type":["string","null"],"description":"[Advanced] Change the domain the controller listens on.\n"},"allow_insecure":{"type":["boolean","null"],"description":"If enabled, accept TLS traffic with an invalid certificate. This is usually done for development/testing, and is not recommended for production use."},"client_cert_auth":{"description":"A PEM encoded string of certificates.","type":["string","null"]},"client_auth":{"description":"Defines how to validate the connecting TLS certificate.\n`none`: Do not require a TLS certificate to be sent\n`request`: Asks the client to send a TLS certificate, but does not require nor validate it.\n`require`: Requires a certificate be sent for the request to be valid, but does not validate the certificate.\n`require-verify`: Requires both that the client send a certificate, and that the certificate is valid. This is required when using https.\n","oneOf":[{"type":"string","enum":["none","request","require","require-verify"]},{"type":"null"}]}}},"timeouts":{"type":"object","description":"Defines how the length of various sorts of timeouts when communicating with the destination.","required":["destination_connection"],"properties":{"destination_connection":{"$ref":"#/properties/scoped_variables/items/properties/access/properties/internal_api/properties/duration/anyOf/0","description":"The duration the load balancer will wait before timing out while attempting to connect to the destination."}}},"extension":{"type":"object","description":"Additional configuration options specific to the selected mode (tcp/http).","discriminator":{"propertyName":"type","mapping":{"tcp":"routers/TcpRouterConfig.yml","udp":"routers/UdpRouterConfig.yml","http":"routers/HttpRouterConfig.yml"}},"oneOf":[{"title":"TcpRouterConfig","description":"Additional configuration options for TCP mode routers","type":"object","required":["type","details"],"properties":{"type":{"type":"string","enum":["tcp"]},"details":{"type":"object","additionalProperties":{}}}},{"title":"HttpRouterConfig","description":"Additional configuration options for HTTP mode routers.","type":"object","required":["type","details"],"properties":{"type":{"type":"string","enum":["http"]},"details":{"type":"object","properties":{"redirect":{"type":["object","null"],"required":["auto_https_redirect","remove_www"],"description":"Defines a built-in redirect for HTTP mode routers","properties":{"auto_https_redirect":{"type":"boolean","description":"If enabled and a sibling controller exists for port 443, requests will be auto redirected to it. Essentially sets up automatic TLS redirection for this router."},"remove_www":{"description":"If true, any request comes in with \"www\" prefix will be permanently redirected to the same path without www.","type":"boolean"},"port":{"type":["integer","null"],"description":"The port to redirect traffic to."},"scheme":{"type":["string","null"],"description":"The scheme to redirect to. (i.e. `https`)"},"url":{"type":["string","null"],"description":"A specific URL to redirect to."}}},"forward":{"type":["object","null"],"properties":{"scheme":{"type":["string","null"]},"content_mod":{"description":"Allows the load balancer to modify content before it reaches the user.","type":["object","null"],"properties":{"replace":{"description":"An array that describes a list of replacement match/value pairs.","type":["array","null"],"items":{"type":"object","required":["match","value"],"properties":{"match":{"description":"String that will be replaced.","type":"string"},"value":{"description":"Replacement value.","type":"string"}}}}}},"url":{"type":"string","description":"The URL to forward the request to.\n\nGiven a path match of `^/example/(.*)$`, a route such as `/example/wow.jpg` would be forwarded as /wow.jpg.\n","examples":["/$$1"]}}},"proxy":{"type":["object","null"],"properties":{"domain":{"type":["string","null"],"description":"The proxy domain for this router."},"content_mod":{"description":"Allows the load balancer to modify content before it reaches the user.","type":["object","null"],"properties":{"replace":{"description":"An array that describes a list of replacement match/value pairs.","type":["array","null"],"items":{"type":"object","required":["match","value"],"properties":{"match":{"description":"String that will be replaced.","type":"string"},"value":{"description":"Replacement value.","type":"string"}}}}}}}},"caching":{"type":["object","null"],"properties":{"files":{"type":["array","null"],"items":{"type":"object","required":["match","ttl"],"properties":{"match":{"description":"Regex string that describes the files to cache.","example":"(.*)\\\\.(js|jpg|css|png|svg)$","type":"string"},"ttl":{"description":"Time string that describes the time to live.","$ref":"#/properties/scoped_variables/items/properties/access/properties/internal_api/properties/duration/anyOf/0"}}}}}}}}}},{"title":"UdpRouterConfig","description":"Additional configuration options for UDP mode routers","type":"object","required":["type","details"],"properties":{"type":{"type":"string","enum":["udp"]},"details":{"type":"object","additionalProperties":{}}}}]}}}}}}}}}}},"controller_template":{"anyOf":[{"$ref":"#/properties/services/properties/loadbalancer/anyOf/1/properties/config/anyOf/0/oneOf/1/properties/details/properties/controllers/items"},{"type":"null"}]},"waf":{"anyOf":[{"title":"WafConfig","type":"object","description":"Additional configuration options for the web application firewall.","required":["rules"],"properties":{"rules":{"type":"array","items":{"type":"object","required":["description","skip","type","conditions"],"properties":{"description":{"type":"string"},"skip":{"type":"boolean"},"type":{"type":"string","enum":["allow","deny"]},"conditions":{"type":"array","items":{"type":"object","required":["type","operator","value"],"properties":{"type":{"type":"string","enum":["ip-match","geo-match","url-match","method-match","header-match"]},"operator":{"type":"string","enum":["==","!=",">","<",">=","<="]},"value":{"type":"string"}}}}}}}}},{"type":"null"}]}}},"bind_host":{"description":"Binds the load balancer to the host server IP address. \n\n**Pros**: This allows for significantly lower cost (utilizing fewer IPv4 addresses), and enables building out a true edge network with lower latency.\n**Cons**: Only 1 environment is allowed on the host. This is because the load balancer is the only ingress point for an environment, and if it is sharing\nthe same IP as the host, that host can only operate under that environment.\n","type":["boolean","null"]}}},{"title":"DefaultLbType","type":"object","required":["type","details","ipv4","ipv6"],"properties":{"ipv4":{"type":"boolean","description":"Allow / disallow traffic to be routed via IPv4."},"ipv6":{"type":"boolean","description":"Allow / disallow traffic to be routed via IPv6."},"type":{"type":"string","enum":["default"]},"details":{"oneOf":[{"$ref":"#/properties/services/properties/loadbalancer/anyOf/1/properties/config/anyOf/0/oneOf/0/properties/details/anyOf/0"},{"$ref":"#/properties/services/properties/loadbalancer/anyOf/1/properties/config/anyOf/0/oneOf/1/properties/details"},{"type":"null"}]}}}]},{"type":"null"}]}}}]},"scheduler":{"anyOf":[{"type":"null"},{"title":"StackSpecSchedulerService","description":"Configuration options for the scheduler service.","type":"object","additionalProperties":false,"properties":{"service":{"anyOf":[{"$ref":"#/properties/services/properties/vpn/anyOf/1/properties/service/anyOf/0"},{"type":"null"}]},"config":{"anyOf":[{"title":"StackSpecSchedulerConfig","description":"Scheduler specific configuration options.","type":["object","null"],"required":["public"],"properties":{"public":{"description":"If true, this scheduler will be accessible over the public internet. It will enable a LINKED record to be pointed to the scheduler container, and inbound requests to trigger function containers.","type":"boolean"},"access_keys":{"type":"array","items":{"title":"StackSpecSchedulerAccessKey","description":"Custom authorization keys for the scheduler service.","type":"object","required":["name","secret"],"properties":{"name":{"type":"string","description":"The name given to this access key."},"secret":{"type":"string","description":"The access key secret. This should be submitted with requests to a publicly accessible scheduler service."},"ips":{"type":"array","description":"Whitelisted IPs that are allowed to make requests to the scheduler service.","items":{"type":"string"}}}}}}},{"type":"null"}]}}}]},"vpn":{"anyOf":[{"type":"null"},{"title":"StackSpecVpnService","description":"Configuration options for the VPN service.","type":"object","additionalProperties":false,"properties":{"service":{"anyOf":[{"title":"StackService","description":"Stack spec configuration options common to all environment services. If one of these properties is defined, all must be.","type":"object","properties":{"enable":{"description":"Whether or not this service should be enabled.","type":"boolean"},"high_availability":{"description":"Whether or not Cycle should run multiple instances of this service for high availability.","type":"boolean"},"auto_update":{"description":"Whether or not Cycle should automatically update this service when a new version is released.","type":"boolean"}}},{"type":"null"}]},"config":{"anyOf":[{"title":"StackSpecVpnConfig","description":"VPN specific configuraiton options.","type":"object","required":["auth","allow_internet"],"properties":{"auth":{"type":"object","required":["cycle_accounts","vpn_accounts"],"properties":{"webhook":{"description":"The endpoint to hit when attempting to authorize a VPN account. If the endpoint returns a 200 response, access is granted. Otherwise it is denied.","type":"string"},"cycle_accounts":{"description":"If true, any account with access to this environment on Cycle can use their Cycle credentials to log in to this VPN.","type":"boolean"},"vpn_accounts":{"description":"If true, Cycle will allow custom accounts to be created for logging into this VPN.","type":"boolean"}}},"allow_internet":{"type":"boolean"}}},{"type":"null"}]}}}]}}},"annotations":{"type":"object","description":"Additional meta info about the stack.","additionalProperties":{}}}}