Schema Reference

Complete schema documentation for .shai/config.yaml.

Top-Level Keys

type

Required: Yes Type: String Value: shai-sandbox

Schema identifier for validation.

1type: shai-sandbox

version

Required: Yes Type: Integer Value: 1

Configuration schema version. Future releases may bump this.

1version: 1

image

Required: Yes Type: String

Base container image to use for sandboxes.

1image: ghcr.io/colony-2/shai-mega

Supports templates: Yes (${{ env.* }}, ${{ vars.* }})

Examples:

1# Official images
2image: ghcr.io/colony-2/shai-base
3image: ghcr.io/colony-2/shai-mega
4
5# Custom image
6image: ghcr.io/my-org/dev-env:latest
7
8# Using environment variable
9image: ${{ env.DEV_IMAGE }}

user

Required: No Default: shai Type: String

Container user Shai switches to before running your command.

1user: developer

Supports templates: Yes (${{ env.* }}, ${{ vars.* }})

CLI override:

1shai --user myuser
This user is created inside the container if it doesn’t exist. The user’s UID/GID are matched to your host user for seamless file permissions.

workspace

Required: No Default: /src Type: String

Absolute path of the repository inside the container.

1workspace: /workspace

Supports templates: Yes (${{ env.* }}, ${{ vars.* }})

Most configurations should stick with the default /src. Only change this if you have a specific reason.

resources

Required: Yes Type: Map of resource set definitions

Defines named resource sets. See Resource Sets below.

1resources:
2  base-allowlist:
3    http: [github.com]
4
5  my-tools:
6    vars: [...]
7    mounts: [...]

apply

Required: Yes Type: List of apply rules

Ordered list that maps workspace paths to resource sets. See Apply Rules below.

1apply:
2  - path: ./
3    resources: [base-allowlist]
4
5  - path: frontend
6    resources: [npm-tools]

Resource Sets

Resource sets are defined under the resources key:

1resources:
2  my-resource-set:
3    vars: [...]
4    mounts: [...]
5    calls: [...]
6    http: [...]
7    ports: [...]
8    root-commands: [...]
9    options: {...}

vars

Type: List of environment variable mappings

Maps host environment variables to container environment variables.

Fields:

  • source: Name of host environment variable (required)
  • target: Name of env var inside container (optional, defaults to source)

Example:

 1resources:
 2  api-keys:
 3    vars:
 4      # When target is omitted, uses same name in container
 5      - source: OPENAI_API_KEY
 6      - source: DATABASE_URL
 7
 8      # Only specify target when you want a different name
 9      - source: MY_HOST_API_KEY
10        target: API_KEY

Behavior:

  • Missing source env vars cause config loading to fail
  • Values are never logged or exposed in config dumps
  • If target is omitted, the variable keeps the same name in the container
  • Only specify target when you need to rename the variable

mounts

Type: List of bind mount specifications

Mounts host directories into the container.

Fields:

  • source: Absolute path on the host (supports templates)
  • target: Absolute path in the container (supports templates)
  • mode: ro (read-only) or rw (read-write)

Example:

 1resources:
 2  cache-mounts:
 3    mounts:
 4      # npm cache
 5      - source: ${{ env.HOME }}/.npm
 6        target: /home/${{ conf.TARGET_USER }}/.npm
 7        mode: rw
 8
 9      # SSL certificates (read-only)
10      - source: /etc/ssl/certs
11        target: /etc/ssl/certs
12        mode: ro
13
14      # SSH keys (read-only)
15      - source: ${{ env.HOME }}/.ssh
16        target: /home/${{ conf.TARGET_USER }}/.ssh
17        mode: ro

Behavior:

  • Non-existent source directories are skipped with a warning
  • Useful for optional mounts that may not exist on all machines
  • Use ${{ conf.TARGET_USER }} in target paths for user-specific directories
Security: Be cautious when mounting sensitive directories. Always use mode: ro for credentials and keys.

calls

Type: List of remote call definitions

Defines host commands that can be invoked from inside the container.

Fields:

  • name: Unique identifier for the call (required)
  • description: Human-readable description (required)
  • command: Absolute path to host command (required)
  • allowed-args: Regex pattern to validate arguments (optional)

Example:

 1resources:
 2  deployment:
 3    calls:
 4      - name: deploy-staging
 5        description: Deploy to staging environment
 6        command: /usr/local/bin/deploy.sh
 7        allowed-args: '^--env=staging --region=us-\w+-\d+$'
 8
 9      - name: trigger-build
10        description: Trigger CI build
11        command: /usr/local/bin/trigger-build.sh
12        # No allowed-args means no arguments permitted

Behavior:

  • Call names must be unique within a single workspace path
  • Arguments are validated against allowed-args regex before execution
  • Missing allowed-args means the call accepts no arguments
  • Calls are invoked with shai-remote <name> [args] inside the container
Security: Always use strict allowed-args patterns to prevent command injection.

See Selective Elevation for more details.


http

Type: List of hostnames

Defines which HTTP/HTTPS destinations are accessible from the container.

Example:

1resources:
2  web-access:
3    http:
4      - github.com
5      - api.openai.com
6      - npmjs.org
7      - pypi.org

Behavior:

  • Only listed domains are accessible
  • Subdomains are automatically included (e.g., github.com allows api.github.com)
  • All other HTTP/HTTPS traffic is blocked
  • Implemented via iptables, dnsmasq, and tinyproxy
Don’t include http:// or https:// prefixes. Just the hostname.

ports

Type: List of host/port pairs

Allows access to specific TCP/UDP ports on specific hosts.

Fields:

  • host: Hostname or IP address
  • port: Port number

Example:

 1resources:
 2  ssh-access:
 3    ports:
 4      - host: github.com
 5        port: 22
 6
 7      - host: gitlab.com
 8        port: 22
 9
10      - host: database.internal
11        port: 5432

Use cases:

  • SSH access to git servers
  • Database connections
  • Custom TCP services

root-commands

Type: List of shell commands

Commands to execute as root before switching to the target user.

Example:

1resources:
2  docker-in-docker:
3    root-commands:
4      - "systemctl start docker"
5      - "modprobe nbd"
6    options:
7      privileged: true

Behavior:

  • Executed sequentially after container setup
  • Run before user switch
  • Any failure causes container to exit
  • Only executed if container starts with root privileges

Use cases:

  • Starting system services
  • Loading kernel modules
  • Configuring network interfaces
Root commands require careful consideration. They run with full root privileges.

options

Type: Object with container-level options

Fields:

  • privileged: Boolean (default: false)

Example:

1resources:
2  gpu-access:
3    options:
4      privileged: true

Behavior:

  • privileged: true runs the container in privileged mode
  • Reduces container isolation
  • Required for Docker-in-Docker, GPU access, etc.
Use privileged: true sparingly! It significantly weakens security.

Apply Rules

Apply rules are defined under the apply key:

1apply:
2  - path: <workspace-relative-path>
3    resources: [<resource-set-names>]
4    image: <optional-image-override>

path

Required: Yes Type: String

Workspace-relative path that activates resource sets.

Examples:

1apply:
2  - path: ./               # Matches everything
3  - path: .                # Same as ./
4  - path: frontend         # Matches frontend/**/*
5  - path: backend/api      # Matches backend/api/**/*

Behavior:

  • Paths are relative to workspace root
  • Matches the path and all subdirectories
  • ./ or . matches the entire workspace

resources

Required: Yes Type: List of strings

Names of resource sets to apply when the path matches.

Example:

1apply:
2  - path: frontend
3    resources: [base-allowlist, npm-registries, playwright]

Behavior:

  • All listed resource sets are activated
  • Resource sets must be defined in the resources section
  • Call names within a path must be unique across all resource sets

image

Required: No Type: String

Optional container image override for this path.

Example:

1apply:
2  - path: ./
3    resources: [base-allowlist]
4    # Uses default image from top-level config
5
6  - path: infrastructure
7    resources: [deployment-tools]
8    image: ghcr.io/my-org/devops:latest

Behavior:

  • Overrides the top-level image key for this path
  • More specific paths take precedence
  • Root path (. or ./) cannot use image overrides
  • CLI --image flag takes ultimate precedence

Template Variables

The following template variables are available in config values:

Environment Variables

1${{ env.NAME }}

References a host environment variable.

Example:

1image: ${{ env.DOCKER_IMAGE }}
2
3resources:
4  api-keys:
5    vars:
6      - source: OPENAI_API_KEY

Behavior:

  • Missing env vars cause config loading to fail
  • Available in all string fields

User-Provided Variables

1${{ vars.NAME }}

References a variable provided via --var flag.

Example:

1# Config
2image: ghcr.io/my-org/dev:${{ vars.TAG }}
3
4# CLI
5shai --var TAG=v1.2.3

Behavior:

  • Missing vars cause config loading to fail
  • Useful for parameterizing configs

Configuration Variables

1${{ conf.TARGET_USER }}
2${{ conf.WORKSPACE }}

References resolved configuration values.

Available:

  • TARGET_USER: The resolved target user (default: shai)
  • WORKSPACE: The resolved workspace path (default: /src)

Example:

1resources:
2  cache:
3    mounts:
4      - source: ${{ env.HOME }}/.cache
5        target: /home/${{ conf.TARGET_USER }}/.cache
6        mode: rw

Limitations:

  • Cannot be used in the user or workspace fields themselves
  • Only available after those fields are resolved

Complete Schema

 1type: shai-sandbox
 2version: 1
 3image: <image-name>
 4
 5# Optional
 6user: <username>
 7workspace: <path>
 8
 9resources:
10  <resource-set-name>:
11    vars:
12      - source: <VAR>           # Uses same name in container
13      - source: <VAR2>
14        target: <NEW_NAME>      # Renames in container
15
16    mounts:
17      - source: <host-path>
18        target: <container-path>
19        mode: ro|rw
20
21    calls:
22      - name: <call-name>
23        description: <description>
24        command: <host-command>
25        allowed-args: <regex>
26
27    http:
28      - <hostname>
29
30    ports:
31      - host: <hostname>
32        port: <port-number>
33
34    root-commands:
35      - <command>
36
37    options:
38      privileged: true|false
39
40apply:
41  - path: <workspace-path>
42    resources: [<resource-set-names>]
43    image: <optional-image-override>

Validation

Shai validates the config when loading:

Checks:

  • Required fields are present
  • type is shai-sandbox
  • version is 1
  • Resource set names are valid
  • Apply rules reference existing resource sets
  • Template variables are defined
  • Paths are valid

Behavior:

  • Invalid configs prevent Shai from starting
  • Validation errors include helpful messages
  • Use shai --verbose to debug config issues

Next Steps