Outcall
SpecificationsS002 · Network Management

S002 · Network Management

Specification module 002-network-management

S002: Network Management

FieldValue
SpecS002
FeatureNetwork Management
Date2026-04-22
StatusImplemented
Author@marktopper

Overview

Manage the isolated networks that agent containers run on. Every agent container connects to an outcall-managed network where all outbound traffic is governed by the bridge and nftables policy layer. The host operator creates, inspects, and destroys these networks through the outcall CLI and the outcalld host API.

A default network (outcall-default) is created on first use and can be destroyed and recreated at any time. Additional named networks can be created for workload isolation — each network shares the same bridge and nftables policy, but provides a separate address space and container group.

Subnet allocation

Outcall reserves the 10.200.0.0/16 block. This range was chosen because it avoids collisions with Docker (172.17+), Kubernetes (10.96, 10.244), AWS VPCs (10.0-10.10), k3s (10.42-10.43), OpenVPN (10.8), home routers (10.0, 192.168), and Podman (10.88). Each network gets a /24 subnet auto-allocated from this block:

outcall-default    → 10.200.0.0/24   (first available)
outcall-staging    → 10.200.1.0/24   (next available)
outcall-prod       → 10.200.2.0/24   (next available)
...up to 256 networks

Before allocating, outcalld checks both its own records and Docker's live network list to avoid collisions. A specific subnet can be requested with --subnet to override auto-allocation.

User Scenarios

S002-US-001 [P1] As a host operator, I want to create isolated networks for agent containers so that each workload group has its own address space.

S002-US-002 [P1] As a host operator, I want to inspect which containers are connected to each network so that I can manage workload placement.

S002-US-003 [P1] As a host operator, I want to destroy networks I no longer need so that resources are cleaned up.

Requirements Summary

IDTypePriorityTitleStatus
S002-FR-001FunctionalP1Docker Engine API onlyImplemented
S002-FR-002FunctionalP1Network creation parametersImplemented
S002-FR-003FunctionalP1Idempotent createImplemented
S002-FR-004FunctionalP1Bridge-up prerequisiteImplemented
S002-FR-005FunctionalP2Post-create bridge verificationImplemented
S002-FR-006FunctionalP1Default network definitionImplemented
S002-FR-007FunctionalP1Default network on bare createImplemented
S002-FR-008FunctionalP1Default network destroyableImplemented
S002-FR-009FunctionalP1Named network prefixImplemented
S002-FR-010FunctionalP1Shared bridge interfaceImplemented
S002-FR-011FunctionalP2Name validationImplemented
S002-FR-012FunctionalP1Subnet auto-allocationImplemented
S002-FR-013FunctionalP1Collision detectionImplemented
S002-FR-014FunctionalP2Subnet exhaustion errorImplemented
S002-FR-015FunctionalP1Explicit subnet collision checkImplemented
S002-FR-016FunctionalP1Status response contentImplemented
S002-FR-017FunctionalP1No state cachingImplemented
S002-FR-018FunctionalP1List endpoint discoveryImplemented
S002-FR-019FunctionalP1Destroy refuses when connectedImplemented
S002-FR-020FunctionalP2Idempotent destroyImplemented
S002-FR-021FunctionalP1No force-disconnectImplemented
S002-FR-022FunctionalP1In-process Docker managementImplemented
S002-FR-023FunctionalP2Docker client startup resilienceImplemented
S002-FR-024FunctionalP1Host socket onlyImplemented
S002-FR-025FunctionalP1CLI subcommandsImplemented
S002-FR-026FunctionalP1CLI transport mechanismImplemented
S002-FR-027FunctionalP1CLI error handlingImplemented
S002-FR-028FunctionalP1Constants in outcall-apiImplemented
S002-FR-029FunctionalP2Configurable subnet blockImplemented
S002-FR-030FunctionalP2RFC 1918 validationImplemented
S002-FR-031FunctionalP2Config query endpointImplemented
S002-FR-032FunctionalP1Network rediscovery on startupImplemented
S002-FR-033FunctionalP1Immutable managed-network identityImplemented
S002-AS-001AcceptanceP1Create default networkImplemented
S002-AS-002AcceptanceP1Create named network (auto-subnet)Implemented
S002-AS-003AcceptanceP1Create already existsImplemented
S002-AS-004AcceptanceP1Create bridge not upImplemented
S002-AS-005AcceptanceP1Status with containersImplemented
S002-AS-006AcceptanceP1Status named networkImplemented
S002-AS-007AcceptanceP1Status does not existImplemented
S002-AS-008AcceptanceP1List multiple networksImplemented
S002-AS-009AcceptanceP1Destroy named networkImplemented
S002-AS-010AcceptanceP1Destroy default networkImplemented
S002-AS-011AcceptanceP1Destroy containers connectedImplemented
S002-AS-012AcceptanceP2Destroy does not existImplemented
S002-AS-013AcceptanceP1Daemon not runningImplemented
S002-AS-014AcceptanceP1Forged prefixed network rejectedImplemented
S002-AS-015AcceptanceP1Overlapping explicit subnet rejectedImplemented
S002-AS-016AcceptanceP1Docker not availableImplemented
S002-IF-001InterfaceP1POST /api/v1/network/createImplemented
S002-IF-002InterfaceP1GET /api/v1/networkImplemented
S002-IF-003InterfaceP1GET /api/v1/networksImplemented
S002-IF-004InterfaceP1POST /api/v1/network/destroyImplemented
S002-IF-005InterfaceP1CLI commandsImplemented
S002-IF-006InterfaceP1CLI output formatImplemented
S002-EC-001Edge CaseP1Network already existsImplemented
S002-EC-002Edge CaseP1Bridge not upImplemented
S002-EC-003Edge CaseP1Docker unreachableImplemented
S002-EC-004Edge CaseP1Status for missing networkImplemented
S002-EC-005Edge CaseP2Destroy missing networkImplemented
S002-EC-006Edge CaseP1Containers connected on destroyImplemented
S002-EC-007Edge CaseP1Destroy default networkImplemented
S002-EC-008Edge CaseP2Bridge mismatchImplemented
S002-EC-009Edge CaseP2Rapid create callsImplemented
S002-EC-010Edge CaseP2Daemon shutdown with networksImplemented
S002-EC-011Edge CaseP2Docker disappears at runtimeImplemented
S002-EC-012Edge CaseP1Subnet collisionImplemented
S002-EC-013Edge CaseP2Subnets exhaustedImplemented
S002-EC-014Edge CaseP2Invalid network nameImplemented
S002-EC-015Edge CaseP1Overlapping non-equal CIDRsImplemented
S002-EC-016Edge CaseP1Invalid private allocation boundaryImplemented
S002-SC-001SuccessP1Default network creation verifiedImplemented
S002-SC-002SuccessP1Named network auto-allocationImplemented
S002-SC-003SuccessP1Status accuracyImplemented
S002-SC-004SuccessP1List completenessImplemented
S002-SC-005SuccessP1Named network destroyImplemented
S002-SC-006SuccessP1Default network destroy+recreateImplemented
S002-SC-007SuccessP1Connected container rejectionImplemented
S002-SC-008SuccessP1IdempotencyImplemented
S002-SC-009SuccessP1Collision-aware allocationImplemented
S002-SC-010SuccessP1No docker CLI shellingImplemented
S002-SC-011SuccessP1Bridge commands unaffectedImplemented

Out of Scope

  • Container lifecycle management (start, stop, attach, exec)
  • Connecting/disconnecting individual containers to/from a network
  • Agent-side API exposure — network management endpoints are host-only
  • TLS or authentication on the host socket
  • Network policy rules (per-container allow/deny) — handled by bridge + nftables
  • DNS configuration within networks
  • IPv6 support
  • Automated bridge-up before network create

Cross-Spec Dependencies

  • Depends on: S001 (bridge must be up before network creation — S002-FR-004)
  • Required by: S006, S007, S008

Shared Types (outcall-api)

Constants

pub const NETWORK_PREFIX: &str = "outcall-";
pub const DEFAULT_NETWORK_NAME: &str = "outcall-default";
pub const SUBNET_BLOCK: &str = "10.200.0.0/16";
pub const DEFAULT_SUBNET: &str = "10.200.0.0/24";
pub const DEFAULT_GATEWAY: &str = "10.200.0.1";

Types

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NetworkCreateRequest {
    pub name: Option<String>,
    pub subnet: Option<String>,
    pub gateway: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NetworkCreateResult {
    pub network_id: String,
    pub name: String,
    pub created: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NetworkStatus {
    pub exists: bool,
    pub network_id: Option<String>,
    pub name: String,
    pub subnet: Option<String>,
    pub gateway: Option<String>,
    pub containers: Vec<NetworkContainer>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NetworkContainer {
    pub name: String,
    pub ipv4_address: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NetworkDestroyRequest {
    pub name: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NetworkDestroyResult {
    pub name: String,
    pub destroyed: bool,
}

On this page