# How Session-Persistent Skill Sandboxes Enforce Per-Tenant Network Policy in WeKnora

> Learn how WeKnora's session-persistent skill sandboxes enforce per-tenant network policy by storing firewall rules and injecting restrictions into container runtimes.

- Repository: [Tencent/WeKnora](https://github.com/tencent/WeKnora)
- Tags: internals
- Published: 2026-09-12

---

**WeKnora enforces per-tenant network policies by storing tenant-specific firewall rules in the sandbox configuration, binding them to persistent sessions via `sandbox_config_id`, and injecting these restrictions into the container runtime during launch.**

Tencent/WeKnora isolates skill execution using **session-persistent sandboxes** that maintain consistent network restrictions across entire user conversations. Unlike ephemeral containers that terminate after each skill call, this architecture ensures that every interaction within a session adheres to the tenant's defined security boundaries while reusing the same isolated environment.

## What Is a Session-Persistent Skill Sandbox?

A session-persistent sandbox is a long-running container instance that remains active for the duration of a user session. According to the source code in [`internal/types/tenant.go`](https://github.com/Tencent/WeKnora/blob/main/internal/types/tenant.go), each session record stores a `sandbox_config_id` that points to a specific tenant sandbox configuration. This foreign key binding ensures that when a session initializes, WeKnora either attaches to an existing sandbox instance or creates a new one that will be reused for all subsequent skill executions in that conversation.

This persistence model eliminates the overhead of container cold starts during multi-turn interactions while maintaining strict isolation guarantees between different tenants.

## How Per-Tenant Network Policy Is Defined

Tenant administrators define network restrictions through the `SandboxNetworkPolicy` struct defined in [`internal/types/sandbox_network_policy.go`](https://github.com/Tencent/WeKnora/blob/main/internal/types/sandbox_network_policy.go). This configuration specifies:

- **Allowed DNS servers**: An array of IP addresses the sandbox can query for domain resolution
- **Egress IP ranges**: CIDR blocks defining permitted outbound destinations
- **Firewall rules**: Implicit deny-all policies for any traffic outside the whitelist

The policy is stored as a JSON object within the tenant's sandbox configuration payload. When creating a sandbox config via the API (handled in [`internal/handler/sandbox_config.go`](https://github.com/Tencent/WeKnora/blob/main/internal/handler/sandbox_config.go)), the `network_policy` field accepts this structure:

```go
type SandboxNetworkPolicy struct {
    DNS      []string `json:"dns"`       // allowed DNS servers
    EgressIP []string `json:"egress_ip"` // allowed outbound IP CIDRs
}

```

Example configuration payload:

```json
{
    "name": "Tenant-A Sandbox",
    "description": "Sandbox with strict egress",
    "config": {
        "sandbox_type": "docker",
        "docker": { "image": "wechatopenai/weknora-sandbox" },
        "network_policy": {
            "dns": ["10.0.0.53"],
            "egress_ip": ["10.0.0.0/24"]
        }
    }
}

```

## Policy Persistence and Database Schema

The network policy persists in the `tenant_sandbox_configs` table, which stores the complete configuration including the `network_policy` JSON object in the `config` column. The database migration [`migrations/versioned/000083_session_sandbox_config.up.sql`](https://github.com/Tencent/WeKnora/blob/main/migrations/versioned/000083_session_sandbox_config.up.sql) establishes this schema, ensuring that tenant-specific sandbox settings survive application restarts and can be referenced across multiple sessions.

This persistence layer decouples policy definition from runtime enforcement, allowing administrators to manage network rules independently of active sandbox instances.

## Session-to-Sandbox Binding

When WeKnora creates a new session, it assigns a `sandbox_config_id` that links the conversation to a specific tenant configuration. As implemented in [`internal/types/tenant.go`](https://github.com/Tencent/WeKnora/blob/main/internal/types/tenant.go), this binding makes the sandbox **session-persistent**—the same container instance services every skill call within that session.

The sandbox manager maintains this association throughout the session lifecycle, ensuring that:
1. All skill executions occur within the same network namespace
2. The tenant's egress whitelist applies consistently across the conversation
3. No cross-tenant traffic leakage occurs between different sandbox instances

## Runtime Enforcement Mechanism

The actual network isolation happens during container initialization in the remote client implementations. The [`internal/sandbox/remote_client.go`](https://github.com/Tencent/WeKnora/blob/main/internal/sandbox/remote_client.go) interface and its Docker-specific implementation [`docker_remote_client.go`](https://github.com/Tencent/WeKnora/blob/main/docker_remote_client.go) read the `NetworkPolicy` from the `SandboxConfig` and translate it into runtime-specific constraints.

### Docker Remote Client Implementation

The `DockerRemoteClient.launchSandbox()` method constructs container arguments that enforce the tenant's network restrictions:

```go
func (c *DockerRemoteClient) launchSandbox(cfg *SandboxConfig) error {
    // cfg.NetworkPolicy is of type SandboxNetworkPolicy
    args := []string{
        "--network", "none",               // start with no network
        "--dns", strings.Join(cfg.NetworkPolicy.DNS, ","),
        // later we attach a custom iptables rule for egress IPs
    }
    // run `docker run` with the above args …
}

```

This implementation launches containers with the `--network none` flag initially, then applies custom iptables rules that restrict outbound traffic to the CIDR ranges specified in `cfg.NetworkPolicy.EgressIP`.

### Network Isolation at Runtime

When the container starts, the underlying runtime (Docker, Cube, or E2B) creates a custom network namespace that:
- Permits DNS queries only to the tenant-specified servers
- Blocks all outbound traffic except to whitelisted IP ranges
- Prevents lateral movement between sandboxes belonging to different tenants

The enforcement happens at the infrastructure level, making it impossible for skills to bypass these restrictions through in-container configuration changes.

## Dynamic Policy Updates

Network policies are not static. When administrators update a tenant's sandbox config via `PUT /sandbox-configs/:id`, the new policy is written immediately to the database. As documented in [`website-docs/04-api/02-api-sandbox-skills.md`](https://github.com/Tencent/WeKnora/blob/main/website-docs/04-api/02-api-sandbox-skills.md), these changes take effect through a rolling restart mechanism:

1. The updated policy is stored in `tenant_sandbox_configs`
2. Existing sandboxes continue with their current restrictions until termination
3. New session sandboxes (or restarted existing ones) receive the updated rules
4. All subsequent skill executions in affected sessions respect the latest egress whitelist

This approach balances immediate policy propagation with session stability, ensuring zero-downtime security updates.

## Summary

- **Policy Definition**: Tenants specify network rules via the `SandboxNetworkPolicy` struct in [`internal/types/sandbox_network_policy.go`](https://github.com/Tencent/WeKnora/blob/main/internal/types/sandbox_network_policy.go), defining allowed DNS servers and egress IP ranges
- **Persistence**: The migration [`000083_session_sandbox_config.up.sql`](https://github.com/Tencent/WeKnora/blob/main/000083_session_sandbox_config.up.sql) stores these policies in the `tenant_sandbox_configs` table alongside sandbox metadata
- **Session Binding**: The `sandbox_config_id` field in [`internal/types/tenant.go`](https://github.com/Tencent/WeKnora/blob/main/internal/types/tenant.go) links sessions to specific tenant configurations, enabling session-persistent isolation
- **Runtime Enforcement**: [`internal/sandbox/remote_client.go`](https://github.com/Tencent/WeKnora/blob/main/internal/sandbox/remote_client.go) and [`docker_remote_client.go`](https://github.com/Tencent/WeKnora/blob/main/docker_remote_client.go) inject these policies into container initialization, creating restricted network namespaces via Docker arguments and iptables rules
- **Dynamic Updates**: API endpoints documented in [`website-docs/04-api/02-api-sandbox-skills.md`](https://github.com/Tencent/WeKnora/blob/main/website-docs/04-api/02-api-sandbox-skills.md) allow real-time policy modifications that apply to new sandboxes without disrupting active sessions

## Frequently Asked Questions

### How does WeKnora store per-tenant network policies?

WeKnora stores network policies as JSON objects within the `config` column of the `tenant_sandbox_configs` table, defined by the migration [`migrations/versioned/000083_session_sandbox_config.up.sql`](https://github.com/Tencent/WeKnora/blob/main/migrations/versioned/000083_session_sandbox_config.up.sql). The `SandboxNetworkPolicy` struct in [`internal/types/sandbox_network_policy.go`](https://github.com/Tencent/WeKnora/blob/main/internal/types/sandbox_network_policy.go) defines the schema, which includes arrays for allowed DNS servers and egress IP CIDR ranges. This approach keeps policy data versioned and tenant-isolated in the relational database.

### What happens when a network policy is updated while sessions are active?

When administrators update a policy via the `PUT /sandbox-configs/:id` endpoint, the changes persist immediately to the database but do not terminate running sandboxes. Existing sessions continue with their current network restrictions until their sandbox instance restarts, while new sessions (or restarted existing ones) receive the updated policy. This rolling approach ensures continuous availability while maintaining eventual consistency with the latest security rules.

### Which container runtimes support these network policies?

The [`internal/sandbox/remote_client.go`](https://github.com/Tencent/WeKnora/blob/main/internal/sandbox/remote_client.go) interface abstracts the policy enforcement, allowing implementations for Docker, Cube, and E2B runtimes. The reference implementation in [`docker_remote_client.go`](https://github.com/Tencent/WeKnora/blob/main/docker_remote_client.go) demonstrates enforcement through Docker's `--network` flags and custom iptables rules. Each runtime adapter translates the `SandboxNetworkPolicy` into platform-specific network isolation mechanisms while maintaining the same tenant-level restrictions.

### How does session persistence maintain network isolation across skill calls?

The `sandbox_config_id` foreign key in the `sessions` table ensures that a single container instance services all skill calls within a conversation. Because the sandbox is created once per session (rather than per skill invocation), the network namespace initialized with the tenant's egress whitelist remains constant throughout the session lifecycle. This persistence model guarantees that every interaction adheres to the same network boundaries without the overhead of repeated container creation.