# How to Adjust Linux Capabilities for Containers in Agent Substrate

> Learn how to adjust Linux capabilities for containers in Agent Substrate by specifying add and drop lists in your ActorTemplate. Control container permissions effectively.

- Repository: [Agent Substrate/substrate](https://github.com/agent-substrate/substrate)
- Tags: how-to-guide
- Published: 2026-08-22

---

**Agent Substrate allows you to adjust Linux capabilities for containers by specifying `add` and `drop` lists in the `Capabilities` field of an `ActorTemplate`, which the runtime processes through the `resolveCapabilities` function in [`cmd/atelet/oci.go`](https://github.com/agent-substrate/substrate/blob/main/cmd/atelet/oci.go) before injecting them into the OCI runtime spec.**

Agent Substrate implements a fine-grained security model that lets you grant specific kernel privileges to actor containers without resorting to full root access. When you adjust Linux capabilities for containers in Agent Substrate, the system translates your declarative template configuration into concrete OCI runtime security profiles through a resolution pipeline that handles defaults, explicit drops, and capability additions.

## How Capability Resolution Works

The capability resolution pipeline centers on the `resolveCapabilities` function defined in [`cmd/atelet/oci.go`](https://github.com/agent-substrate/substrate/blob/main/cmd/atelet/oci.go) (lines 52-78). This function bridges the gap between user-friendly template specifications and the low-level OCI runtime requirements.

### Default Capability Set

Agent Substrate starts with a conservative default set of capabilities assigned to every container:

- **AUDIT_WRITE** – Allows writing to the audit log
- **KILL** – Permits sending signals to processes
- **NET_BIND_SERVICE** – Enables binding to privileged ports (below 1024)

These defaults provide basic functionality while minimizing the attack surface. The system stores these as unprefixed strings internally, then prepends the OCI-standard `CAP_` prefix during resolution.

### The Resolution Algorithm

The `resolveCapabilities` function implements a three-stage resolution process:

1. **Initialize** with the default set (`AUDIT_WRITE`, `KILL`, `NET_BIND_SERVICE`)
2. **Process drops** – Removes any capability listed in the template's `Drop` field; the special value `"ALL"` clears the entire set
3. **Process adds** – Appends any capability from the template's `Add` field
4. **Normalize** – Prefixes each capability with `CAP_` and sorts the slice for deterministic OCI specs

This logic ensures that drops take precedence over defaults, and explicit adds override any previous state. The final sorted slice guarantees stable OCI bundle generation across reconciliations.

## Configuring Capabilities in Actor Templates

You adjust capabilities at the template level using the v1alpha1 `ActorTemplate` resource. The `Capabilities` message supports two string arrays: `add` and `drop`. You specify capabilities without the `CAP_` prefix; the resolver handles normalization automatically.

### YAML Configuration Example

```yaml
apiVersion: substrate.agent/v1alpha1
kind: ActorTemplate
metadata:
  name: privileged-network-actor
spec:
  capabilities:
    add:
      - "SYS_ADMIN"
      - "NET_ADMIN"
    drop:
      - "NET_BIND_SERVICE"
  image: "my-registry/network-tool:latest"
  command: ["/bin/sh", "-c", "ip link set eth0 up"]

```

In this example, the resulting container receives `CAP_AUDIT_WRITE`, `CAP_KILL`, `CAP_SYS_ADMIN`, and `CAP_NET_ADMIN`, but lacks `CAP_NET_BIND_SERVICE` despite it being part of the default set. To drop all capabilities and start from a blank slate, specify `drop: ["ALL"]` before adding specific privileges.

## OCI Spec Injection

After resolution, the `buildActorOCISpec` function (lines 300-321 of [`cmd/atelet/oci.go`](https://github.com/agent-substrate/substrate/blob/main/cmd/atelet/oci.go)) injects the capability slice into the OCI runtime specification. The function populates three key fields of the OCI `specs.Process.Capabilities` struct:

- **Bounding** – Limits which capabilities the process can gain
- **Effective** – Controls which capabilities are currently active
- **Permitted** – Defines the maximum set of capabilities the process can assume

Agent Substrate sets all three fields to the identical resolved slice for consistency, while explicitly leaving **Inheritable** empty. This mirrors the security posture of containerd, CRI-O, and Docker, preventing capability leakage to child processes during exec operations.

## gVisor Worker Defaults

For unprivileged workloads running under gVisor, the controller applies a predefined capability set through `ateomGvisorCapabilities` in [`cmd/atecontroller/internal/controllers/workerpool_apply.go`](https://github.com/agent-substrate/substrate/blob/main/cmd/atecontroller/internal/controllers/workerpool_apply.go) (lines 238-275). This builder uses `WithCapabilities` to attach capabilities such as `CAP_NET_RAW` and `CAP_SYS_CHROOT` to worker pods, ensuring sandboxed actors receive the specific privileges required for network operations and filesystem isolation without granting full container capabilities.

When creating worker pods, the controller translates these Go constants into Kubernetes `corev1.Capability` objects and attaches them to the container security context:

```go
workerPod.Spec.Containers[0].SecurityContext.Capabilities = &corev1.Capabilities{
    Add: ateomGvisorCapabilities,
}

```

## Validation and Testing

The capability resolution behavior is validated by unit tests in [`internal/e2e/suites/capabilities/capabilities_test.go`](https://github.com/agent-substrate/substrate/blob/main/internal/e2e/suites/capabilities/capabilities_test.go) (lines 34-48). These tests verify that:

- Default capabilities are present when no modifications are specified
- The `"ALL"` sentinel correctly clears the capability set
- Explicit adds append to the final slice
- Drops remove entries from both defaults and adds

Running these tests ensures that changes to the resolution logic maintain backward compatibility with existing templates and security policies.

## Summary

- **Template-driven configuration** – Adjust capabilities by modifying the `Capabilities` field in `ActorTemplate` resources (v1alpha1), using unprefixed capability names
- **Resolution logic** – The `resolveCapabilities` function in [`cmd/atelet/oci.go`](https://github.com/agent-substrate/substrate/blob/main/cmd/atelet/oci.go) processes drops before adds, starting from the default set of `AUDIT_WRITE`, `KILL`, and `NET_BIND_SERVICE`
- **OCI compliance** – Capabilities are normalized with `CAP_` prefixes and injected into the OCI spec's `Bounding`, `Effective`, and `Permitted` fields by `buildActorOCISpec`
- **Security defaults** – gVisor workers receive a specialized capability set defined in [`cmd/atecontroller/internal/controllers/workerpool_apply.go`](https://github.com/agent-substrate/substrate/blob/main/cmd/atecontroller/internal/controllers/workerpool_apply.go) to support sandboxed execution
- **Testing coverage** – End-to-end tests in [`internal/e2e/suites/capabilities/capabilities_test.go`](https://github.com/agent-substrate/substrate/blob/main/internal/e2e/suites/capabilities/capabilities_test.go) validate the resolution pipeline against regression

## Frequently Asked Questions

### How do I drop all default capabilities and start from zero?

Specify `drop: ["ALL"]` in your ActorTemplate's capabilities section. This sentinel value tells the `resolveCapabilities` function to clear the entire default set before processing any `add` entries. You can then selectively add only the specific capabilities your container requires, implementing a "deny-by-default" security posture.

### What is the difference between the default capabilities and gVisor worker capabilities?

Default capabilities apply to standard container workloads managed by the atelet runtime, providing basic sandbox functionality. gVisor worker capabilities, defined in [`cmd/atecontroller/internal/controllers/workerpool_apply.go`](https://github.com/agent-substrate/substrate/blob/main/cmd/atecontroller/internal/controllers/workerpool_apply.go), are specific to the gVisor sandbox controller and include additional privileges like `CAP_NET_RAW` required for the sandbox's networking implementation. Worker capabilities are applied at the Kubernetes pod level, while template capabilities are processed into the OCI spec.

### Why does Agent Substrate leave the Inheritable capability set empty?

Agent Substrate mirrors the security model of containerd, CRI-O, and Docker by leaving `Inheritable` capabilities empty. This prevents privilege escalation during exec operations, ensuring that child processes cannot inherit capabilities from the parent container unless explicitly granted through the OCI runtime configuration. This containment strategy reduces the risk of capability leakage in multi-process containers.

### Can I use capability names with the CAP_ prefix in my templates?

No. The `resolveCapabilities` function expects unprefixed capability names in the template (e.g., `SYS_ADMIN` rather than `CAP_SYS_ADMIN`). The resolver automatically prepends `CAP_` during OCI spec generation. Including the prefix in your template will result in double-prefixed values like `CAP_CAP_SYS_ADMIN`, which the kernel will reject as invalid capabilities.