Resource Sets

Resource Sets are named collections of resources that define what agents can access inside the sandbox.

What Are Resource Sets?

A resource set is a collection of:

  • HTTP/HTTPS destinations - Which websites the agent can reach
  • Host mounts - Which host directories to mount into the container
  • Environment variables - Which env vars to pass from host to container
  • Ports - Which TCP/UDP ports to allow (e.g., SSH)
  • Remote calls - Which host commands can be invoked from inside the sandbox
  • Root commands - Commands to run as root before switching to the agent user
  • Options - Container-level options like privileged mode

Why Resource Sets?

Different coding tasks need different resources:

TaskNeeds
Frontend developmentnpm registries, CDNs, Playwright
Backend developmentDatabase connections, API testing
DeploymentCloud APIs, SSH access, credentials
ML/AI developmentModel downloads, GPU access

Resource sets let you define these collections once and reuse them across your project.

Defining Resource Sets

Resource sets are defined in .shai/config.yaml:

 1# .shai/config.yaml
 2resources:
 3  base-allowlist:
 4    http:
 5      - github.com
 6      - npmjs.org
 7      - pypi.org
 8
 9  frontend-dev:
10    http:
11      - cdn.jsdelivr.net
12      - unpkg.com
13    mounts:
14      - source: ${{ env.HOME }}/.cache/playwright
15        target: /home/${{ conf.TARGET_USER }}/.cache/playwright
16        mode: rw
17
18  deployment:
19    vars:
20      - source: AWS_ACCESS_KEY_ID
21    http:
22      - amazonaws.com
23      - s3.amazonaws.com
24    calls:
25      - name: deploy-staging
26        description: Deploy to staging environment
27        command: /usr/local/bin/deploy.sh
28        allowed-args: '^--env=staging$'

Resource Set Components

HTTP/HTTPS Destinations

Allow agents to access specific websites:

1resources:
2  my-tools:
3    http:
4      - api.openai.com
5      - github.com
6      - registry.npmjs.org

How it works:

  • Shai configures a proxy (tinyproxy) and DNS filtering (dnsmasq)
  • Only listed domains are accessible
  • All other network traffic is blocked
You don’t need to specify subdomains separately. github.com also allows api.github.com, raw.githubusercontent.com, etc.

Host Mounts

Mount host directories into the container:

1resources:
2  cache-mount:
3    mounts:
4      - source: ${{ env.HOME }}/.cache/models
5        target: /home/${{ conf.TARGET_USER }}/.cache/models
6        mode: rw
7      - source: /etc/ssl/certs
8        target: /etc/ssl/certs
9        mode: ro

Fields:

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

Use cases:

  • Sharing package caches (npm, pip, cargo)
  • Sharing model weights
  • Sharing SSL certificates
  • Sharing SSH keys (read-only!)
Non-existent source directories are skipped with a warning. This is useful for optional mounts.

Environment Variables

Pass environment variables from host to container:

1resources:
2  api-keys:
3    vars:
4      - source: OPENAI_API_KEY
5      - source: ANTHROPIC_API_KEY

Security note: Variables are mapped from host env, not hardcoded in config. This prevents credentials from being committed to git.

The target field is optional. If omitted, the variable keeps the same name in the container. Only specify target when you need to rename the variable.
Missing environment variables cause config loading to fail. This is intentional - you’ll catch missing credentials early.

Ports

Allow access to specific TCP/UDP ports:

1resources:
2  ssh-access:
3    ports:
4      - host: github.com
5        port: 22
6      - host: gitlab.com
7        port: 22

Use cases:

  • SSH to git servers
  • Connecting to databases
  • Accessing custom services

Remote Calls

Allow agents to invoke specific host commands:

1resources:
2  firmware-dev:
3    calls:
4      - name: flash-device
5        description: Flash firmware to connected device
6        command: /usr/local/bin/flash.sh
7        allowed-args: '^--port=/dev/tty\w+$'

Fields:

  • name: Unique name for the call
  • description: Human-readable description
  • command: Absolute path to host command
  • allowed-args: (Optional) Regex to filter arguments

Inside the sandbox:

1# Agent can invoke the call
2shai-remote flash-device --port=/dev/ttyUSB0

See Selective Elevation for more details.

Root Commands

Run commands as root before switching to the agent user:

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

Use cases:

  • Starting system services
  • Loading kernel modules (modprobe)
  • Setting up network interfaces
Root commands only run when the container starts with root privileges. If the container starts as a non-root user, these commands are skipped.

Options

Container-level options:

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

Available options:

  • privileged: Run container in privileged mode (reduces isolation)
Use privileged: true sparingly! It significantly reduces container security.

Template Expansion

Resource set fields support template variables:

1resources:
2  my-tools:
3    vars:
4      - source: MY_HOST_KEY    # Host env var
5        target: API_KEY        # Renamed in container
6    mounts:
7      - source: ${{ env.HOME }}/.cache
8        target: /home/${{ conf.TARGET_USER }}/.cache  # Container user
9        mode: rw

Available templates:

  • ${{ env.NAME }} - Host environment variable
  • ${{ vars.NAME }} - Variable provided via --var flag
  • ${{ conf.TARGET_USER }} - The resolved target user (default: shai)
  • ${{ conf.WORKSPACE }} - The resolved workspace path (default: /src)

Using Resource Sets

Via Apply Rules

Resource sets are typically activated via apply rules:

1apply:
2  - path: ./
3    resources: [base-allowlist]
4
5  - path: frontend
6    resources: [frontend-dev, playwright]
7
8  - path: infrastructure
9    resources: [deployment, cloud-apis]

When you run shai -rw frontend/components, Shai automatically applies base-allowlist, frontend-dev, and playwright.

Via CLI Flag

You can also opt into resource sets manually:

1shai -rw src --resource-set gpu-access

This adds gpu-access to whatever resource sets are resolved via apply rules.

Resource Set Aggregation

When multiple rules match a path, their resource sets are aggregated (deduplicated):

1apply:
2  - path: ./
3    resources: [base-allowlist]
4
5  - path: backend
6    resources: [database-access]
7
8  - path: backend/payments
9    resources: [stripe-api]

Running shai -rw backend/payments gets all three:

  1. base-allowlist (from .)
  2. database-access (from backend)
  3. stripe-api (from backend/payments)

Best Practices

✅ Do

  • Create focused, single-purpose resource sets
  • Use descriptive names (frontend-dev, not tools)
  • Map env vars instead of hardcoding credentials
  • Document why each resource is needed (via comments)
  • Start minimal and add resources as needed

❌ Don’t

  • Create one giant “everything” resource set
  • Hardcode secrets in the config
  • Grant privileged mode unless absolutely necessary
  • Mount sensitive directories as read-write

Examples

Frontend Development

 1resources:
 2  frontend-dev:
 3    http:
 4      - cdn.jsdelivr.net
 5      - fonts.googleapis.com
 6      - unpkg.com
 7    mounts:
 8      - source: ${{ env.HOME }}/.npm
 9        target: /home/${{ conf.TARGET_USER }}/.npm
10        mode: rw

Python ML Development

 1resources:
 2  ml-dev:
 3    http:
 4      - huggingface.co
 5      - files.pythonhosted.org
 6    mounts:
 7      - source: ${{ env.HOME }}/.cache/huggingface
 8        target: /home/${{ conf.TARGET_USER }}/.cache/huggingface
 9        mode: rw
10    vars:
11      - source: HUGGINGFACE_TOKEN

Kubernetes Deployment

 1resources:
 2  k8s-deploy:
 3    vars:
 4      - source: KUBECONFIG
 5    mounts:
 6      - source: ${{ env.HOME }}/.kube
 7        target: /home/${{ conf.TARGET_USER }}/.kube
 8        mode: ro
 9    http:
10      - kubernetes.default.svc
11    calls:
12      - name: apply-manifests
13        description: Apply Kubernetes manifests
14        command: /usr/local/bin/kubectl-apply.sh
15        allowed-args: '^--namespace=\w+$'

Next Steps