Skip to main content
  1. IO Reference/

IO Configuration Reference

2304 words·11 mins
Agent IO
Author
Agent IO
Table of Contents
Here’s how you can configure IO using its HCL-based configuration language.

Built on HCL
#

IO configuration is based on the Hashicorp Configuration Language. This provides a level of readability that far surpasses Envoy’s YAML-based configuration, of course at the cost of flexibility. But with its tighter focus, IO’s HCL configuration seems right for the high-level capabilities that IO provides.

IO configuration file structure
#

IO’s configuration can be divided among any number of files, and these can be read into IO in any order. When it is convenient to include elements in the same file, that’s great, and when it’s helpful to split files into pieces for readability or reuse, that’s also great.

Calling configuration
#

These elements configure IO’s calling mode.

calling blocks
#

Specify APIs to be supported by calling mode.

calling "google-translate" {
  name     = "Google Translate"
  target   = "translate.googleapis.com"
  port     = 8000
  protocol = "http2"
  apply_header "authorization" {
    service_account = "translate"
    scopes          = ["https://www.googleapis.com/auth/cloud-platform"]
  }
  require_jwt {
    jwks_url = "https://tokens.babu.dev/jwks.json"
  }
}

The id value
#

String. Provided as the first argument to the calling declaration, this is a machine-friendly name of the API to be called.

The name value
#

String. This is an optional human-friendly name of the API.

The target value
#

String. This is the address of the API service to be called. By default, this is expected to be available over HTTPS when port 443 is specified in the address or when no port is named and HTTP for all other addresses.

The insecure value
#

Boolean. When set to true, this disables certificate verification for HTTPS targets.

The port value
#

Integer. This is the local port on which the calling interface will listen. When no port is specified, the calling interface will listen on a Linux abstract socket.

The protocol value
#

String. This value can be http1, http2, or unspecified. It specifies the protocol that Envoy should use when connecting to the target service.

apply_header blocks
#

When provided, these blocks configure IO to add headers to calls to the target service. The values in these blocks are described below.

idrequiredSpecified as the apply_header argument, this is the name of the header to add
valueoptionalIf provided, this specifies the constant value that should be used as the header value
secretoptionalIf provided, this specifies the id of a secret whose value should be provided as the header value
service_accountoptionalIf provided, this specifies the id of a secret whose value should be assumed to be a service account key that is used to generate a token to send as the header value
scopesoptionalIf provided, this specifies the scopes that should be used with the service account to generate a token to be sent as the header value

apply_query blocks
#

When provided, these blocks configure IO to add query parameters to calls to the target service. The values in these blocks are described below.

idrequiredSpecified as the apply_query argument, this is the name of the query parameter to add
valueoptionalIf provided, this specifies the constant value that should be used as the query parameter value
secretoptionalIf provided, this specifies the id of a secret whose value should be provided as the query parameter value

The require_jwt block
#

When provided, this block configures IO to require JWT authentication of calls to this calling interface. This secures the calling interface from unwanted callers. The values in these blocks are described below.

jwks_urlrequiredThis specifies the address of a JWKS that will be used to verify the JWTs passed to the calling interface

operation blocks
#

When provided, these blocks describe API operations that can be used to categorize traffic and provide additional access controls. The values in these blocks are described below.

idrequiredSpecified as the operation argument, this is a machine-friendly identifier of the operation
nameoptionalThis is a human-friendly name of the operation
methodrequiredThis is the HTTP method of the operation
pathrequiredThis is the HTTP path of the operation
descriptionoptionalThis is a human-friendly description of the operation
apply_headeroptionalThese blocks follow the form and function of apply_header blocks in calling blocks

secret blocks
#

Specify secrets that can be used when APIs are called. These secrets are stored in IO’s local database. As noted in the apply_header discussion of calling blocks, secrets can also be read from an external secrets manager like Hashicorp’s Vault.

secret "keycheck" {
  value = "smash"
}

The id value
#

String. Provided as the first argument to the secret declaration, this is the secret ID and is used to reference the secret in calling configuration.

The value value
#

String. This is the secret value. This can be a multiline string like the private_key and contents values in certificate blocks.


Serving configuration
#

These elements configure IO’s serving mode.

serving blocks
#

Specify APIs to be supported by serving mode.

serving "nomad" {
  name    = "nomad"
  backend = "127.0.0.1:4646"
  port    = 9009
  require_atproto {
    handles = ["agent.io"]
  }
}

The id value
#

String. Provided as the first argument to the serving declaration, this is a machine-friendly name of the API to be served.

The name value
#

String. This is an optional human-friendly name of the API.

The backend value
#

String. This is the address of the API backend.

The port value
#

Integer. This is the local port on which the serving interface will listen. When no port is specified, the serving interface will listen on a Linux abstract socket.

The protocol value
#

String. This value can be http1, http2, or unspecified. It specifies the protocol that Envoy should use when connecting to the backend service.

The require_basic block
#

When provided, this configures IO to require callers to authenticate with HTTP Basic Auth to use the backend service. The values in these blocks are described below.

usersrequiredA list of users and passwords in htpasswd format

The require_atproto block
#

When provided, this configures IO to require callers to authenticate with ATProto to use the backend service. Only one of handles and dids needs to be specified. The values in these blocks are described below.

handlesrequiredAn array of ATProto handles of authenticated users who are allowed to use the backend service
didsrequiredAn array of ATProto DIDs of authenticated users who are allowed to use the backend service

The require_apikey block
#

When provided, this configures IO to require callers to authenticate with an API key to use the backend service. The values in these blocks are described below.

usersoptionalA list of users and API keys in htpasswd format
authorityoptionalA block describing an API that can be used to verify API keys
excludeoptionalA list of operations to exclude from API key checking

The grpc_json block
#

Specifies configuration to allow JSON transcoding of gRPC services. The values in these blocks are described below.

servicesrequiredAn array listing the gRPC services to be made available using transcoding

Ingress configuration
#

These elements configure IO’s ingress mode.

ingress blocks
#

Specify HTTP- and HTTPS-based services to be supported by IO ingress.

ingress "nomad.agentio.dev" {
  name    = "nomad"
  backend = "serving:nomad"
  atproto_client {
    name   = "Nomad"
    scopes = ["atproto"]
    key    = "*****REDACTED*****"
  }
}

The id value
#

String. Provided as the first argument to the ingress declaration, this is the domain name of the ingress.

The name value
#

String. This is an optional human-friendly name of the service.

The backend value
#

String. This is the address of the ingress backend.

The protocol value
#

String. This value can be http1, http2, or unspecified. It specifies the protocol that Envoy should use when connecting to the backend service.

oauth_client blocks
#

Specifies OAuth client configuration to allow the IO ingress to handle OAuth-based authentication using external OAuth providers. The values in these blocks are described below.

nameoptionalThe name of the client
client_idrequiredThe OAuth client ID that was provided by the provider when the calling application was registered with the provider
client_secretrequiredThe OAuth client secret that was provided by the provider when the calling application was registered with the provider
authorize_urlrequiredThe authorization URL of the provider
access_token_urlrequiredThe token access URL of the provider
scopesoptionalAn array of OAuth scopes requested by the client

The atproto_client block
#

Specifies ATProto client configuration to allow the IO ingress to handle ATProto-based authentication using external ATProto identity providers including Bluesky and independently-run Personal Data Servers. The values in these blocks are described below.

nameoptionalThe name of the client that is provided to the identity provider
keyoptionalThe private key to be used by the client. Typically this is automatically generated and can be specified here to allow keys to be reused
scopesrequiredAn array of ATProto scopes requested by the client

The http_port element
#

Integer. Specifies the HTTP port used by the IO ingress (defaults to 80).

http_port = 80

The https_port element
#

Integer. Specifies the HTTPS port used by the IO ingress (defaults to 443).

https_port = 443

certificate blocks
#

Specify certificates used to provide HTTPS services on IO ingresses. Typically certificates are not specified in user configuration but are instead retrieved using ACME or generated by IO and self-signed. certificate blocks allow these certificates to be exported and reused.

certificate "example.com" {
  domain      = "example.com"
  authority   = "LetsEncrypt"
  private_key = <<END
-----BEGIN RSA PRIVATE KEY-----
******************************
********** REDACTED **********
******************************
-----END RSA PRIVATE KEY-----
END
  contents    = <<END
-----BEGIN CERTIFICATE-----
******************************
********** REDACTED **********
******************************
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
******************************
********** REDACTED **********
******************************
-----END CERTIFICATE-----
END
}

The id value
#

String. Provided as the first argument to the certificate declaration, this is the domain name that the certificate describes.

The authority value
#

String. This identifies the authority used to sign the certificate.

The private_key value
#

String. This is the private key associated with the certificate in PEM format.

The contents value
#

String. This is the certificate contents in PEM format.


acme_provider blocks
#

Specify information needed by remote ACME providers. Currently only the letsencrypt provider is supported.

acme_provider "letsencrypt" {
  name  = "LetsEncrypt"
  email = "someone@example.com"
}

The id value
#

String. Provided as the first argument to the acme_provider declaration, this is provider ID. Currently only letsencrypt is supported.

The name value
#

String. A human-friendly name of the provider.

The email value (required for LetsEncrypt)
#

String. An email address that is registered with the ACME provider.


block blocks
#

Specify IP address ranges to be blocked by IO ingresses.

block "123.45.67.89" {
  prefix_len  = 32
  description = "manually blocked from traffic (GET /index.php)"
}

The id value
#

String. Provided as the first argument to the block declaration, this is network prefix of the address range to block. Frequently this is a full IP address, although ranges can be specified using the prefix_len argument below. This is the part on the left of a CIDR representation.

The prefix_len value
#

Integer. This is the part on the right of a CIDR representation of the range of addresses to block. If unspecified, 32 is used, which blocks only the exact address specified.

The description value
#

String. A free-form description of the block, typically providing a reason for blocking this address range.


SSH configuration
#

These elements configure IO’s SSH-based configuration and control interface.

The ssh_port element
#

Integer. Specifies the port where IO listens for SSH connections (defaults to 8022). Changes to this value take effect on the next restart of IO.

ssh_port = 8022

The host_key element
#

String. Specifies the host key used by the IO SSH server. Typically host keys are automatically generated by IO and are not specified by users. The host_key element allows them to be exported and reused. Changes to this value take effect on the next restart of IO.

host_key = <<END
-----BEGIN PRIVATE KEY-----
**************************
******** REDACTED ********
**************************
-----END PRIVATE KEY-----
END

user blocks
#

Specify SSH users that can connect to IO using SSH.

user "someone@example.com" {
  name = "IO User"
  public_key = "ssh-rsa ****REDACTED**** someone@example.com"
}

The id value
#

String. Provided as the first argument to the user declaration, this is a machine-friendly unique user id.

The name value
#

String. A human-friendly name of the user.

The public_key value
#

String. Typically this is the contents of a user’s ~/.ssh/id_rsa.pub or ~/.ssh/id_ed25519.pub files.


IO Licensing
#

During the private preview, IO requires a license.

The license element
#

String. Specifies an IO license key. License keys can be downloaded here.

license = "**** REDACTED ****"

Reading and writing IO configuration
#

The -c option reads configuration on startup
#

IO can read configuration files on startup when they are specified with the -c flag. Any number of -c flags can be provided. If -x is also specified, IO will immediately exit after configurations are read, leaving an initialized io.db, which retains configuration for subsequent runs of IO.

The terminal UI can read and write local files
#

On the main screen of the IO terminal UI, the r and w keys can trigger IO to read and write local configuration files.

SFTP allows reading and writing to remote IOs
#

Remote IOs can be configured with SFTP. Any file with the .hcl extension is treated as IO configuration when it is copied to IO with SFTP or SCP. For example, this applies a new calling configuration to an IO running at REMOTE:

scp -P 8022 calling.hcl REMOTE:

Configuration can also be read with SFTP and SCP. For example, this copies all of the configuration from an IO running at REMOTE:

scp -r -P 8022 REMOTE:config .

Comment with ATProto
#