Moby — Interfaces#

Sampling strategy#

Moby is an XL project (2000+ Go files) with 289 non-test interface definitions. This analysis focused on the architecturally critical interface families identified from the prior architecture result. Files sampled:

  • client/client_interfaces.go — the complete client-side API contract
  • daemon/image_service.go — the ImageService dual-implementation gateway
  • daemon/internal/libcontainerd/types/types.go — runtime abstraction layer
  • daemon/graphdriver/driver.go — legacy storage driver hierarchy
  • daemon/internal/layer/layer.go — layer management (read-only and read-write)
  • daemon/volume/volume.go — volume driver abstraction
  • daemon/logger/logger.go — logging driver contract
  • daemon/libnetwork/driverapi/driverapi.go — network driver API
  • daemon/libnetwork/ipamapi/contract.go — IPAM (IP Address Management) API
  • daemon/container/store.go — container registry
  • daemon/cluster.go — Swarm cluster facade
  • daemon/server/router/router.go — HTTP routing system
  • daemon/server/middleware/middleware.go — HTTP middleware chain
  • daemon/server/router/container/backend.go — fine-grained container API split
  • daemon/builder/builder.go — build system abstraction
  • errdefs/defs.go — error classification system
  • pkg/plugingetter/getter.go — plugin access abstraction

Interface catalog#

ImageService#

  • Package: github.com/moby/moby/v2/daemon
  • File: daemon/image_service.go
  • Methods (35): Full image lifecycle — PullImage, PushImage, CreateImage, ImageDelete, ExportImage, LoadImage, Images, CountImages, ImagePrune, ImportImage, TagImage, GetImage, ImageHistory, CommitImage, SquashImage, ImageInspect, ImageDiskUsage, LogImageEvent; layer operations — GetImageAndReleasableLayer, CreateLayer, CreateLayerFromImage, GetLayerByID, LayerStoreStatus, GetLayerMountID, ReleaseLayer, GetContainerLayerSize, Changes; build support — MakeImageCache, CommitBuildStep; utilities — DistributionServices, Children, Cleanup, StorageDriver, UpdateConfig
  • Purpose: Single contract point for all image and layer operations, enabling dual storage backend coexistence (legacy graphdriver vs. modern containerd snapshotter). The comment calls it “a temporary interface to assist in the migration to the containerd image-store.”
  • Implementations: daemon/images.imageService (legacy graphdriver + bbolt), daemon/containerd.imageService (containerd-native). Selected at startup by determineImageStoreChoice().
  • Design quality: This interface is intentionally too broad — 35 methods spanning images, layers, distribution, build support, and utilities. It is an architectural migration seam rather than a clean abstraction. The “temporary” comment acknowledges this, but the interface has been stable for years. Violates Interface Segregation Principle by design: the two implementations share the same surface even though they delegate very differently internally.

libcontainerd.Client#

  • Package: github.com/moby/moby/v2/daemon/internal/libcontainerd/types
  • File: daemon/internal/libcontainerd/types/types.go
  • Methods (3): Version(ctx) (containerd.Version, error), LoadContainer(ctx, containerID) (Container, error), NewContainer(ctx, containerID, spec, shim, runtimeOptions, ...opts) (Container, error)
  • Purpose: Minimal gateway to the containerd runtime, returning Container objects for further operation. Deliberately narrow — just enough to create and load containers. All deeper operations are on the returned typed interfaces.
  • Implementations: daemon/internal/libcontainerd/remote.client (gRPC to external containerd, the primary path), daemon/internal/libcontainerd/local.client (deprecated in-process containerd)
  • Design quality: Excellent ISP adherence. The interface itself is small (3 methods). Richness lives in the returned Container and Task types, which are themselves interfaces. Clean separation of concerns: the Client does not expose task or process operations directly.

libcontainerd.Container#

  • Package: github.com/moby/moby/v2/daemon/internal/libcontainerd/types
  • File: daemon/internal/libcontainerd/types/types.go
  • Methods (4): NewTask(ctx, checkpointDir, withStdin, attachStdio) (Task, error), Task(ctx) (Task, error), AttachTask(ctx, attachStdio) (Task, error), Delete(ctx) error
  • Purpose: Represents a containerd container record (metadata, spec). Does not imply a running process — that requires a Task. The comment on AttachTask explicitly warns that only one reader may be attached at a time.
  • Design quality: Well-segregated. Container lifecycle (create/delete) is separate from process lifecycle (task). The StdioCallback type alias for the I/O attachment function is a clean design for stdio wiring.

libcontainerd.Task#

  • Package: github.com/moby/moby/v2/daemon/internal/libcontainerd/types
  • File: daemon/internal/libcontainerd/types/types.go
  • Methods (12 + 4 from Process): Embeds Process (Pid, Kill, Resize, Delete); adds Start, Pause, Resume, Stats, Pids, Summary, ForceDelete, Status, Exec, UpdateResources, CreateCheckpoint
  • Purpose: Represents a running process inside a container. Includes process control, monitoring, and exec (creating sub-processes). Exec returns a Process, creating a clean hierarchy: Task → Process.
  • Design quality: Good embedding — Task IS-A Process, which is semantically correct. The inclusion of CreateCheckpoint (CRIU) slightly broadens the interface but is justified by the feature’s tight coupling to task state.

libcontainerd.Backend#

  • Package: github.com/moby/moby/v2/daemon/internal/libcontainerd/types
  • File: daemon/internal/libcontainerd/types/types.go
  • Methods (1): ProcessEvent(containerID string, event EventType, ei EventInfo) error
  • Purpose: Callback interface from the libcontainerd layer back to the daemon. When containerd signals an event (exit, OOM, pause, exec), libcontainerd calls ProcessEvent on the Backend, which is implemented by daemon.Daemon. Classic dependency inversion: the lower layer (libcontainerd) depends on an interface owned by itself, not on the concrete upper layer (daemon.Daemon).
  • Design quality: Exemplary — minimum viable callback surface (1 method). The entire event protocol is reduced to a single method with an enum + struct payload.

daemon.ImageService (router-level backend splits)#

The daemon/server/router/ packages further decompose the ImageService contract for the HTTP layer. Each sub-interface is defined consumer-side:

  • imageBackend (7 methods): ImageDelete, ImageHistory, ImageInspect, etc.
  • importExportBackend (3 methods): ExportImage, LoadImage, ImportImage
  • registryBackend (2 methods): PullImage, PushImage
  • Searcher (1 method): SearchRegistryForImages
  • The combined Backend for the image router embeds all four.

This is an excellent example of Interface Segregation at the API layer: handlers for image export don’t need to compile against the full ImageService; they only see importExportBackend.


graphdriver.Driver#

  • Package: github.com/moby/moby/v2/daemon/graphdriver
  • File: daemon/graphdriver/driver.go
  • Methods (14): Composes ProtoDriver (10 methods: String, CreateReadWrite, Create, Remove, Get, Put, Exists, Status, GetMetadata, Cleanup) + DiffDriver (4 methods: Diff, Changes, ApplyDiff, DiffSize)
  • Purpose: The legacy union filesystem abstraction for content-addressable layer storage. Implementations include overlay2, btrfs, zfs, and vfs. The split between ProtoDriver and DiffDriver allows clients that only need basic operations to depend on the smaller interface.
  • Implementations: daemon/graphdriver/overlay2, daemon/graphdriver/btrfs, daemon/graphdriver/zfs, daemon/graphdriver/vfs, daemon/graphdriver/windows/windowsgraphdriver
  • Design quality: The interface is mature and stable. DiffGetterDriver is an optional extension interface (Driver + DiffGetter) for tar-split optimization — a correct use of optional interface widening via type assertion rather than mandatory extension.

layer.Layer and layer.RWLayer#

  • Package: github.com/moby/moby/v2/daemon/internal/layer
  • File: daemon/internal/layer/layer.go
  • Layer methods (8): TarStream, TarStreamFrom, ChainID, DiffID, Parent, Size, DiffSize, Metadata — embeds TarStreamer
  • RWLayer methods (9): TarStream, Name, Parent, Mount, Unmount, Size, Changes, Metadata, ApplyDiff — embeds TarStreamer
  • Purpose: Layer models a read-only, content-addressable filesystem snapshot. RWLayer extends it with write capability, mounting, and change tracking. The content-addressability through ChainID/DiffID (SHA256 digests) is intrinsic to the interface.
  • layer.Store methods (13): Full CRUD for both layer types — Register, Get, Map, Release, CreateRWLayer, GetRWLayer, GetMountID, ReleaseRWLayer, Cleanup, DriverStatus, DriverName
  • Design quality: TarStreamer extraction (1 method) is good ISP. The DescribableStore extension interface (for OCI descriptors) is a correct optional extension rather than polluting the base interface.

volume.Driver and volume.Volume#

  • Package: github.com/moby/moby/v2/daemon/volume
  • File: daemon/volume/volume.go
  • Driver methods (6): Name, Create, Remove, List, Get, Scope
  • Volume methods (7): Name, DriverName, Path, Mount, Unmount, CreatedAt, Status
  • Purpose: Plugin-friendly volume system. Driver creates and manages volumes; Volume represents a mountable data store. DetailedVolume extends Volume with Labels, Options, Scope for richer introspection. LiveRestorer is a single-method optional interface for volume drivers that support daemon live-restore.
  • Implementations: Built-in local driver, plus any external volume plugin implementing the Docker Volume Plugin protocol (JSON over Unix socket, mediated by volumedriver.proxy).
  • Design quality: Clean, minimal, well-separated. LiveRestorer as an optional interface (type-asserted, not required) is excellent ISP discipline. Scope is part of the Driver, not the Volume, which is a subtle but correct modeling decision.

logger.Logger#

  • Package: github.com/moby/moby/v2/daemon/logger
  • File: daemon/logger/logger.go
  • Logger methods (3): Log(*Message) error, Name() string, Close() error
  • SizedLogger methods (1 + Logger): embeds Logger + BufSize() int
  • LogReader methods (1): ReadLogs(context.Context, ReadConfig) *LogWatcher
  • Purpose: Logger is the minimum viable contract for a write-only log driver (e.g., json-file, journald, splunk, awslogs, fluentd). LogReader is a separate optional interface for drivers that support log retrieval. The separation is critical: most drivers can’t read back logs.
  • Implementations: daemon/logger/jsonfilelog, daemon/logger/journald, daemon/logger/splunk, daemon/logger/awslogs, daemon/logger/fluentd, daemon/logger/loggerutils.LogFile (for LogReader)
  • Design quality: Excellent ISP. A 3-method write interface + optional 1-method read interface is minimal and correct. SizedLogger extension (buffer size control for performance tuning) is correctly separated. The LogWatcher channel pair (Msg chan *Message, Err chan error) is a clean consumer-driven backpressure mechanism.

driverapi.Driver (network driver)#

  • Package: github.com/moby/moby/v2/daemon/libnetwork/driverapi
  • File: daemon/libnetwork/driverapi/driverapi.go
  • Methods (9): CreateNetwork, DeleteNetwork, CreateEndpoint, DeleteEndpoint, EndpointOperInfo, Join, Leave, Type, IsBuiltIn
  • Purpose: Core contract for network drivers in libnetwork. Covers the full network + endpoint lifecycle. Join/Leave model sandbox attachment.
  • Optional extension interfaces:
    • TableWatcher (2 methods): for gossip-layer table notifications (Swarm overlay driver)
    • ExtConner (1 method): external connectivity programming (gateway tracking)
    • IPv6Releaser (1 method): IPv6 address release when disabled post-creation
    • GwAllocChecker (1 method): skip gateway allocation for special networks
    • NetworkAllocator (3 methods): Swarm cluster resource allocation
  • Implementations: bridge, overlay, macvlan, ipvlan, host, null, remote (plugin), windows
  • Design quality: The core interface (9 methods) is appropriately sized for a network driver. The four optional extension interfaces (TableWatcher, ExtConner, IPv6Releaser, GwAllocChecker) are all type-asserted at call sites — excellent progressive enhancement. NetworkInfo and InterfaceInfo callback interfaces used during endpoint creation are clever: rather than returning complex structs, the driver calls methods on these injected objects to set up the network state.

ipamapi.Ipam#

  • Package: github.com/moby/moby/v2/daemon/libnetwork/ipamapi
  • File: daemon/libnetwork/ipamapi/contract.go
  • Methods (6): GetDefaultAddressSpaces, RequestPool, ReleasePool, RequestAddress, ReleaseAddress, IsBuiltIn
  • Purpose: IP Address Management contract. The PoolRequest/AllocatedPool value types (not interfaces) carry the structured request/response, keeping the interface methods clean.
  • PoolStatuser (extends Ipam + PoolStatus): optional pool status reporting for drivers that support it.
  • Design quality: Well-structured. The PoolRequest struct uses typed fields (netip.Prefix for the Exclude list) rather than stringly-typed options maps, showing modern Go idiom.

server/router.Router and router.Route#

  • Package: github.com/moby/moby/v2/daemon/server/router
  • File: daemon/server/router/router.go
  • Router methods (1): Routes() []Route
  • Route methods (3): Handler() APIFunc, Method() string, Path() string
  • Purpose: Decompose HTTP routing across resource packages. Each package (container, image, network, volume, etc.) implements Router to register its routes with the main server mux. Handlers receive a Backend interface specific to their resource rather than the entire *daemon.Daemon.
  • Implementations: 12 resource routers: container, image, network, volume, build, system, swarm, plugin, distribution, checkpoint, session, grpc, debug
  • Design quality: Textbook single-responsibility routing. Router has only 1 method. Route has 3 methods (HTTP method, path, and handler). The ExperimentalRoute marker interface (zero methods — pure type assertion tag) is used to prevent experimental routes from being registered unless experimental mode is enabled.

middleware.Middleware#

  • Package: github.com/moby/moby/v2/daemon/server/middleware
  • File: daemon/server/middleware/middleware.go
  • Methods (1): WrapHandler(APIFunc) APIFunc
  • Purpose: Functional middleware wrapping — each middleware transforms one handler function into another. Three implementations: ExperimentalMiddleware, VersionMiddleware, authorization.Middleware.
  • Design quality: Elegant 1-method interface. The function-type argument and return type is identical, making the chaining pattern trivial. The authorization.Middleware is the heaviest — it serializes request/response body, forwards to plugin sockets, and returns a modified response — all hidden behind 1 method.

container.Store#

  • Package: github.com/moby/moby/v2/daemon/container
  • File: daemon/container/store.go
  • Methods (7): Add, Get, Delete, List, Size, First, ApplyAll
  • Purpose: In-memory registry of all daemon-known containers. First(StoreFilter) and ApplyAll(StoreReducer) use function types for flexible querying/iteration without exposing implementation details.
  • Implementations: memdb.containerMemDB (backed by hashicorp/go-memdb)
  • Design quality: Concise and appropriate. The functional StoreFilter/StoreReducer types are more flexible than enum-based query options while remaining type-safe.

daemon.Cluster (facade)#

  • Package: github.com/moby/moby/v2/daemon
  • File: daemon/cluster.go
  • Methods (5, via embedding):
    • ClusterStatus: IsAgent(), IsManager()
    • NetworkManager: GetNetwork, GetNetworks, RemoveNetwork
    • Direct: SendClusterEvent
  • Purpose: Facade that daemon.Daemon uses to interact with the SwarmKit cluster subsystem. Separates Swarm status queries from network management. The daemon.cluster.Cluster concrete type implements this interface, but the daemon never depends on the concrete type — it only holds a Cluster interface value.
  • Design quality: The split into ClusterStatus + NetworkManager sub-interfaces allows test fakes to be targeted. However, the façade is deliberately thin — more specific Swarm operations (service management, task scheduling) happen through the daemon/server/router/swarm.Backend interface defined in the swarm router package.

client.APIClient (composite)#

  • Package: github.com/moby/moby/v2/client
  • File: client/client_interfaces.go
  • Structure: APIClient embeds stableAPIClient + CheckpointAPIClient. stableAPIClient embeds 12 resource-specific interfaces + utility methods. Each resource interface has 3–30 methods.
  • Sub-interfaces: ContainerAPIClient (30 methods), ImageAPIClient (12 methods), NetworkAPIClient (7 methods), VolumeAPIClient (6 methods), SystemAPIClient (5 methods), ExecAPIClient (5 methods), SwarmManagementAPIClient (embeds 5 Swarm sub-interfaces), etc.
  • Purpose: Full client-side contract for the Docker Engine API. Any code needing to talk to a Docker daemon depends on APIClient (or a sub-interface for narrower usage).
  • Design quality: The segregation into resource-specific sub-interfaces (ContainerAPIClient, ImageAPIClient, etc.) is excellent for testing and code organization — a function that only lists containers depends on ContainerAPIClient, not APIClient. The CheckpointAPIClient is separated because checkpoints are experimental — same strategy as ExperimentalRoute in the server.

builder.Backend#

  • Package: github.com/moby/moby/v2/daemon/builder
  • File: daemon/builder/builder.go
  • Methods (7 + 5 from embedded): Embeds ImageBackend (GetImageAndReleasableLayer) + ExecBackend (5 exec methods); adds CommitBuildStep, ContainerCreateWorkdir, CreateImage, MakeImageCache
  • Purpose: Abstraction over daemon operations needed by the legacy Dockerfile builder. The ImageCache interface (1 method: GetCache) and ImageCacheBuilder (1 method: MakeImageCache) are minimal contracts for build cache lookups.
  • ROLayer / RWLayer sub-interfaces: Within the builder package, ROLayer and RWLayer are thin wrappers specifically for build contexts, distinct from the layer.Layer / layer.RWLayer interfaces in daemon/internal/layer. This package-local re-definition allows the builder to be implemented against either the legacy or modern image path.
  • Design quality: Appropriate size for the build system’s needs. The separation of ImageBackend from ExecBackend allows testing build steps that don’t need execution.

errdefs — Error classification interfaces#

  • Package: github.com/moby/moby/v2/errdefs
  • File: errdefs/defs.go
  • Interfaces (13): ErrNotFound, ErrInvalidParameter, ErrConflict, ErrUnauthorized, ErrUnavailable, ErrForbidden, ErrSystem, ErrNotModified, ErrNotImplemented, ErrUnknown, ErrCancelled, ErrDeadline, ErrDataLoss
  • Methods: Each interface has exactly 1 method (a marker method like NotFound(), Conflict(), etc.)
  • Purpose: Classify errors by kind without requiring string matching. A function can check errors.As(err, &errdefs.ErrNotFound{}) or use the errdefs.IsNotFound(err) helper. The HTTP server maps these to status codes. Replaces the old pkg/errors stringly-typed error classification.
  • Design quality: Single-method marker interfaces are idiomatic Go for error classification. The pattern is mirrored by containerd’s cerrdefs package, enabling cross-system compatibility.

plugingetter.PluginGetter#

  • Package: github.com/moby/moby/v2/pkg/plugingetter
  • File: pkg/plugingetter/getter.go
  • Methods (4): Get(name, capability, mode), GetAllByCap(capability), GetAllManagedPluginsByCap(capability), Handle(capability, callback)
  • Purpose: Abstraction over the v2 plugin store for subsystems (libnetwork, IPAM, volume) that need to discover plugins by capability type (e.g., "NetworkDriver", "IpamDriver", "VolumeDriver"). The mode int parameter (Lookup/Acquire/Release constants) embeds reference counting into the Get call.
  • Supporting interfaces: CompatPlugin (4 methods — handles both v1 and v2 plugins), CountedPlugin (extends with Acquire/Release), PluginAddr (for custom protocol clients)
  • Design quality: Clean abstraction. The Handle method (register a callback for plugin activation by capability) enables dynamic plugin discovery without polling. Reference counting built into Get mode is slightly unusual but prevents a two-step pattern.

Interface patterns#

Size distribution#

  • 1 method: Backend (libcontainerd callback), Middleware, Router, TarStreamer, LogReader, LiveRestorer — all marker or minimal-callback types. 12 of the errdefs classification interfaces.
  • 2–5 methods: Most “leaf” interfaces — Route, ClusterStatus, NetworkManager, layer.TarStreamer, container.Store, volume.Volume, logger.Logger, ipamapi.Ipam
  • 6–15 methods: graphdriver.ProtoDriver, graphdriver.DiffDriver, layer.Store, layer.RWLayer, driverapi.Driver, container.Store, client sub-interfaces
  • 15–35 methods: ImageService (35), client.ContainerAPIClient (30), libcontainerd.Task (16) — the large “aggregate” interfaces
  • Average for stable plugin APIs: ~5–8 methods. Well within reasonable bounds.

Embedding#

Interface embedding is used pervasively and consistently:

  • graphdriver.Driver = ProtoDriver + DiffDriver (capability composition)
  • libcontainerd.Task embeds Process (IS-A relationship)
  • daemon.Cluster embeds ClusterStatus + NetworkManager (façade composition)
  • client.APIClient embeds 12 sub-interfaces (full client assembly)
  • volume.DetailedVolume embeds Volume (enrichment)
  • logger.SizedLogger embeds Logger (optional capability extension)
  • driverapi.NetworkAllocator uses composition rather than Driver extension for Swarm-specific operations

Implicit satisfaction#

Moby uses both consumer-defined and provider-defined interfaces:

  • Consumer-defined (most common): daemon/server/router/container/backend.go defines execBackend, copyBackend, stateBackend etc. — interfaces defined by the consumer (router handler) listing only the daemon.Daemon methods it needs. This is the idiomatic Go ISP approach.
  • Provider-defined: Plugin driver interfaces (graphdriver.Driver, driverapi.Driver, volume.Driver, logger.Logger) are defined once and implemented by both built-in and external plugins. This is necessary because external plugins are registered dynamically.
  • Cross-module (client): client.APIClient is provider-defined (in the client module) and implemented by client.Client. External code can create test doubles by implementing the sub-interfaces.

stdlib interfaces used#

  • io.Reader, io.ReadCloser, io.Writer, io.ReadWriteCloser — throughout distribution, layer, volume
  • io.Closer — embedded in logger and other resource holders
  • net.Connclient.HijackDialer returns net.Conn for stream hijacking
  • fmt.Stringer — not explicitly depended on, but graphdriver.ProtoDriver.String() returns the driver name string (not via fmt.Stringer interface)
  • http.Handler — indirectly through APIFunc which has the same functional shape but with a context parameter added
  • context.Context — pervasive, passed to virtually every method that touches I/O or external systems

Key abstractions#

The 5 most architecturally significant interfaces:

1. daemon.ImageService — The migration seam#

The single most architecturally consequential interface in Moby. It exists to allow two radically different storage implementations (legacy graphdriver + bbolt metadata + layer chain, vs. modern containerd content store + snapshotter) to coexist behind a single contract. Its breadth (35 methods) is a deliberate trade-off: it enabled the containerd snapshotter path to be developed and deployed incrementally without breaking the daemon. The “temporary” comment has been there for years — the interface will likely outlive most other things in this codebase.

2. libcontainerd.{Client, Container, Task, Process} — The runtime abstraction chain#

This four-level interface hierarchy (Client → Container → Task → Process) cleanly models the OCI/containerd lifecycle. The separation allows the daemon to create container records without starting processes, start processes without caring about runtime implementation details, and handle process events through the inverted Backend callback. This chain is what made the transition from LXC → libcontainer → containerd possible over Docker’s history.

3. driverapi.Driver (network) + ipamapi.Ipam — The plugin protocol pair#

These two interfaces, combined with their optional extension interfaces (TableWatcher, ExtConner, etc.), define the complete extensibility surface of libnetwork. Any code implementing driverapi.Driver can be registered as a network driver — whether it’s the built-in bridge driver, the overlay Swarm driver, or an external remote plugin. The callback interfaces (NetworkInfo, InterfaceInfo, JoinInfo) used during network/endpoint creation are an interesting inversion: instead of the driver returning a rich struct, it receives a callback object and calls methods on it to register state.

4. client.APIClient (composite) — The testability surface#

The decomposed client interface hierarchy (ContainerAPIClient, ImageAPIClient, etc.) is the primary mechanism for testing Docker client code. Any test that exercises Docker CLI logic can inject a minimal fake implementing only the relevant sub-interface. The stableAPIClient / CheckpointAPIClient split isolates experimental features from the stable contract. This is the most user-facing interface family — third-party integrations depend on it.

5. errdefs error classification interfaces — The HTTP/domain bridge#

The 13 single-method marker interfaces in errdefs are architecturally small but operationally critical. They bridge domain error semantics (ErrNotFound, ErrConflict) to HTTP status codes without encoding HTTP knowledge into domain logic. The pattern enables daemon.Daemon to return typed errors, daemon/server to map them to status codes, and the client to re-wrap them back into typed errors — a round-trip of error semantics across the HTTP boundary.


Interface-driven extensibility#

Moby has four distinct plugin / extensibility systems, all interface-driven:

1. Built-in driver plugins (graphdriver, volume, logger, network, IPAM)#

Each system defines a Driver interface in a driverapi or *api sub-package. Drivers register themselves via an InitFunc (graphdriver), Register (volume), plugingetter.Handle (logger), or driverapi.Registerer (network). Built-in drivers live under daemon/graphdriver/, daemon/libnetwork/drivers/, daemon/logger/. External plugins proxy through the remote/ sub-package (HTTP-over-Unix-socket).

2. V2 Docker plugins (daemon/pkg/plugin)#

The daemon.pkg.plugin.Manager loads OCI bundles as plugin runtimes (containerd-managed). The plugingetter.CompatPlugin interface unifies v1 (HTTP direct) and v2 (managed OCI) plugins. Subsystems get plugins via PluginGetter.Get(name, capability, mode). The capability string is the namespace: "NetworkDriver", "VolumeDriver", "LoggingDriver", "AuthorizationPlugin", "IpamDriver".

3. Authorization plugins (pkg/authorization)#

The authorization.Plugin interface exposes Name() and AuthZReq/AuthZRes (request/response authorization callbacks). Plugins are loaded by the daemon and called via authorization.Middleware. The interface is deliberately simple: every API request/response is forwarded as JSON to the plugin over a Unix socket. This is Moby’s primary extensibility point for security policy.

4. Router-per-resource + Backend injection (HTTP API)#

The Router/Route/Backend pattern is an extensibility mechanism for the HTTP API itself. Each resource package defines its own Backend interface, which daemon.Daemon satisfies implicitly. Third-party forks or test harnesses can inject alternative backends by implementing those interfaces. This is not plugin-based but is the most Go-idiomatic form of the pattern.