@emulates/docker 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,392 @@
1
+ # Docker Engine API evidence
2
+
3
+ US-002 research, retrieved **2026-09-25 UTC**. Status: documentation and pinned
4
+ source inspected; **historical research status, superseded by the US-013 live
5
+ run recorded below**.
6
+ This file defines a planned subset, not an implemented support claim. See
7
+ [boundaries and scenario IDs](../../../docs/INFRASTRUCTURE_MOCKS.md).
8
+
9
+ ## Compatibility target and provenance
10
+
11
+ - API target: **v1.52**. The downloaded reference declares Swagger 2.0,
12
+ `info.version: 1.52`, `basePath: /v1.52`.
13
+ - Historical proposed oracle (superseded by user direction in US-013):
14
+ **Docker Engine 29.1.0**, Linux, default API settings.
15
+ This is a reproducible compatibility pin, not a recommendation to downgrade a
16
+ host or a claim of availability. No default Docker socket was queried.
17
+ - Verified upstream tag: `docker-v29.1.0`, commit
18
+ `710302ecf2e958db92cb7d92f8838ea063a31765`. The initial guessed `v29.1.0`
19
+ tag returned 404; it is not the source pin.
20
+ - Pinning evidence: [release tag object](https://api.github.com/repos/moby/moby/git/tags/ab0910fb285e342a43491868a095052ac24308d8),
21
+ [pinned tree](https://github.com/moby/moby/tree/710302ecf2e958db92cb7d92f8838ea063a31765).
22
+ - Versioned docs [D1](https://docs.docker.com/reference/api/engine/version/v1.52.yaml)
23
+ SHA-256: `893db6d64dad76a0662e33557f1689f7b394cbf1a2fa2d1a9dc5e2e2edbe0e25`.
24
+ - Pinned specification [S1](https://github.com/moby/moby/blob/710302ecf2e958db92cb7d92f8838ea063a31765/api/swagger.yaml)
25
+ SHA-256: `69fe12ce2c6ef1e42a317e5c3d60c9e0dbc5bf4f30e56472ab947975d9a6eedb`.
26
+ Both YAML files were fetched and parsed. Their selected path objects agree
27
+ except that D1 adds a `400` response to ContainerStart; S1 omits it.
28
+
29
+ Immutable source references used below:
30
+
31
+ | ID | Source | Evidence used |
32
+ | --- | --- | --- |
33
+ | S2 | [Version middleware](https://github.com/moby/moby/blob/710302ecf2e958db92cb7d92f8838ea063a31765/daemon/server/middleware/version.go) | Default version for unversioned calls; version errors and response headers |
34
+ | S3 | [Server routing](https://github.com/moby/moby/blob/710302ecf2e958db92cb7d92f8838ea063a31765/daemon/server/server.go) | Versioned/unversioned route registration; error formatting; unknown path/method |
35
+ | S4 | [Container routes](https://github.com/moby/moby/blob/710302ecf2e958db92cb7d92f8838ea063a31765/daemon/server/router/container/container_routes.go) | Attach handshake/errors; wait response timing; stop completion |
36
+ | S5 | [Daemon configuration](https://github.com/moby/moby/blob/710302ecf2e958db92cb7d92f8838ea063a31765/daemon/config/config.go) | Maximum 1.52, default minimum 1.44; configurable floor down to 1.24 |
37
+ | S6 | [Container stop](https://github.com/moby/moby/blob/710302ecf2e958db92cb7d92f8838ea063a31765/daemon/stop.go) | Stop waits for termination; cancelling the request does not undo the stop |
38
+ | S7 | [Container state](https://github.com/moby/moby/blob/710302ecf2e958db92cb7d92f8838ea063a31765/daemon/container/state.go) | Wait conditions, immediate results and cancellation |
39
+ | S8 | [Container attach](https://github.com/moby/moby/blob/710302ecf2e958db92cb7d92f8838ea063a31765/daemon/attach.go) | Multiplexed output, raw input, OpenStdin/StdinOnce, paused/restarting conflicts |
40
+
41
+ ## Context7 research record
42
+
43
+ Resolved `Docker` with the query “Docker Engine API v1.52 container lifecycle and
44
+ HTTP attach upgrade protocol official documentation.” Selected `/docker/docs`
45
+ because it is the official docs repository. Resolver advertised only
46
+ `__branch__main`, not a pinned v1.52 library version. Three queries were made;
47
+ their results are discovery evidence, not v1.52 verification:
48
+
49
+ | Query scope | Returned source/version | Claim and reconciliation |
50
+ | --- | --- | --- |
51
+ | v1.52 attach upgrade, 101, framing, stdin and TTY | [v1.19](https://github.com/docker/docs/blob/main/_vendor/github.com/moby/moby/api/docs/v1.19.md), [v1.23](https://github.com/docker/docs/blob/main/_vendor/github.com/moby/moby/api/docs/v1.23.md), v1.11/v1.17/v1.18 excerpts | Historical raw-stream/upgrade examples. Superseded for this subset by D1/S1 and S4/S8: non-TTY v1.52 upgrade uses multiplexed-stream. |
52
+ | v1.52 create/start/stop/kill/wait/remove lifecycle statuses and conditions | [current SDK examples](https://github.com/docker/docs/blob/main/content/reference/api/engine/sdk/_index.md), [v1.16 stop](https://github.com/docker/docs/blob/main/_vendor/github.com/moby/moby/api/docs/v1.16.md), v1.0/v1.20 excerpts | General create/start/wait and old stop statuses; insufficient for current lifecycle semantics. D1/S1 and S4/S6/S7 supply the pin. |
53
+ | v1.52 discovery, unversioned calls, version rejection, inspect/list | [CLI deprecations](https://github.com/docker/docs/blob/main/_vendor/github.com/docker/cli/docs/deprecated.md), v1.22/v1.44 excerpts; unrelated Docker Agent `/api/ping` | Historical guidance deprecates unversioned calls, but S2/S3 still register/default them in this pin. Docker Agent ping is unrelated and excluded. |
54
+
55
+ The [current API matrix](https://docs.docker.com/reference/api/engine/#api-version-matrix)
56
+ maps Engine 29.0/29.1 to API 1.52. S5, rather than current-main prose, establishes
57
+ the exact selected daemon's defaults. Refresh Context7 and primary evidence before
58
+ adding operation families or changing versions, and before US-010 transport work.
59
+
60
+ ## Version routing and rejection policy
61
+
62
+ US-004 refresh (2026-09-27): Context7 `/docker/docs` still exposes only main.
63
+ List/inspect queries returned v1.4/v1.6/v1.56 excerpts; info/rootless queries
64
+ returned v1.12/v1.20/current docs. None replaces the pinned 1.52 source.
65
+ Newly inspected pinned sources are
66
+ [daemon/list.go](https://github.com/moby/moby/blob/710302ecf2e958db92cb7d92f8838ea063a31765/daemon/list.go),
67
+ [daemon/inspect.go](https://github.com/moby/moby/blob/710302ecf2e958db92cb7d92f8838ea063a31765/daemon/inspect.go),
68
+ [filters/parse.go](https://github.com/moby/moby/blob/710302ecf2e958db92cb7d92f8838ea063a31765/daemon/internal/filters/parse.go), and
69
+ [httputils/form.go](https://github.com/moby/moby/blob/710302ecf2e958db92cb7d92f8838ea063a31765/daemon/server/httputils/form.go).
70
+ List uses creation-descending order, AND label matching, OR status/exit values,
71
+ unique ID-prefix selection, and name regex matching. A status filter or positive
72
+ limit includes non-running containers. Exited filtering requires a stopped
73
+ container that has started. JSON filter maps accept legacy arrays and boolean
74
+ sets (keys matter even if false); null denotes an empty map. Boolean query
75
+ parsing is permissive: trimmed empty/0/no/false/none are false, other values true.
76
+ The emulator bounds name regex support as documented in README and returns explicit
77
+ 501 for other patterns/filters. No installed Engine or differential run was used.
78
+ Pinned [container lookup](https://github.com/moby/moby/blob/710302ecf2e958db92cb7d92f8838ea063a31765/daemon/container.go)
79
+ and [prefix lookup](https://github.com/moby/moby/blob/710302ecf2e958db92cb7d92f8838ea063a31765/daemon/container/view.go)
80
+ confirm full ID, exact name, then unique prefix precedence; ambiguous prefixes
81
+ return InvalidParameter (400), while missing containers return 404.
82
+ List status descriptions follow pinned S7 and its
83
+ [duration formatter](https://github.com/moby/moby/blob/710302ecf2e958db92cb7d92f8838ea063a31765/vendor/github.com/docker/go-units/duration.go),
84
+ using the injected emulator clock rather than host process uptime.
85
+
86
+ S2/S3 explain why the observed `/v1.52/containers/{id}/attach` and unversioned
87
+ `/info` can belong to one client: every provider route is registered with and
88
+ without a version, and an absent version uses the daemon default (1.52 here).
89
+ Deprecation guidance is not evidence that this pinned daemon rejects `/info`.
90
+
91
+ Planned emulator policy: support v1.52 and unversioned aliases for the listed subset.
92
+ Do not claim to emulate all older APIs merely because the real Engine accepts them.
93
+ For numeric versions above 1.52, use the provider's too-new `400` error; versions
94
+ below the default minimum 1.44 use its too-old `400` error. Versions 1.44–1.51
95
+ are **unsupported by this emulator subset**: return an explicitly Emulates-labelled
96
+ `501` message, not a fictional Engine rejection. Historical compatibility can only
97
+ be expanded with new evidence. Document these emulator-only 501 responses in codegen.
98
+
99
+ Provider version-error message templates from S2 are:
100
+
101
+ ```text
102
+ client version <v> is too new. Maximum supported API version is 1.52
103
+ client version <v> is too old. Minimum supported API version is 1.44, please upgrade your client to a newer version
104
+ ```
105
+
106
+ S3 serializes errors as `{"message":"..."}` except requests below API 1.24,
107
+ which receive plain text. Unknown paths/methods use `404` with
108
+ `{"message":"page not found"}`. Malformed version paths are not a promise of
109
+ version negotiation. No automatic client reconnection or fallback is part of
110
+ the emulator. Its declared API support must remain distinct from simulated Engine
111
+ `MinAPIVersion` metadata.
112
+
113
+ ## Planned operation contract
114
+
115
+ Paths below are relative to `/v1.52`, also available unversioned as above.
116
+ The status column is the pinned S1 vendor contract; additional implementation
117
+ errors supported by pinned source must be annotated explicitly, not silently
118
+ invented. Global version errors and emulator-only unsupported-feature errors are
119
+ separate from this table. Unless stated otherwise, vendor errors use
120
+ `ErrorResponse` with required string `message`.
121
+
122
+ | Method/path (operation ID) | Inputs and bounded planned behavior | Vendor statuses |
123
+ | --- | --- | --- |
124
+ | GET/HEAD `/_ping` (SystemPing/SystemPingHead) | GET body `OK`, HEAD empty; API-Version, Builder-Version, Docker-Experimental, Swarm and cache headers | 200, 500 |
125
+ | GET `/version` (SystemVersion) | Version, ApiVersion, MinAPIVersion, Os, Arch, GitCommit and system/build metadata; simulation labelled in package docs | 200, 500 |
126
+ | GET `/info` (SystemInfo) | Synthetic daemon identity, container counts, operating-system and security observations; availability independent of container state | 200, 500 |
127
+ | GET `/containers/json` (ContainerList) | `all` defaults false, `limit`, `size`; JSON `filters` map. Initial filters: id, name, status, label, exited. Other filters explicitly unsupported | 200, 400, 500 |
128
+ | GET `/containers/{id}/json` (ContainerInspect) | Container ID/name; `size` defaults false. Coherent Id, Name, Image, Config, HostConfig, State and NetworkSettings | 200, 404, 500 |
129
+ | POST `/containers/create` (ContainerCreate) | `name`, `platform`, ContainerConfig plus HostConfig/NetworkingConfig; seeded local image lookup, name uniqueness, immutable Id and Warnings | 201, 400, 404 (image missing), 409 (conflict), 500 |
130
+ | POST `/containers/{id}/start` (ContainerStart) | ID/name; no execution. Supported transition and already-running result; detachKeys/checkpoint features outside initial subset | 204, 304, 404, 500; D1 additionally documents 400 |
131
+ | POST `/containers/{id}/stop` (ContainerStop) | ID/name, signal, integer timeout `t`; pending graceful termination then completion | 204, 304, 404, 500 |
132
+ | POST `/containers/{id}/kill` (ContainerKill) | ID/name, signal (default SIGKILL); supported signal observations, no host signal | 204, 404, 409 (not running), 500 |
133
+ | POST `/containers/{id}/wait` (ContainerWait) | ID/name, condition omitted/empty => not-running; also next-exit and removed | 200, 400, 404, 500 |
134
+ | DELETE `/containers/{id}` (ContainerDelete) | ID/name, force; `v`/`link` default false. Volumes/legacy links outside subset | 204, 400, 404, 409, 500 |
135
+ | POST `/containers/{id}/attach` (ContainerAttach) | Node upgrade only; stream=true, logs=false, stdin/stdout/stderr selections, Tty=false | 101 (upgrade), 200 (real non-upgrade, unsupported here), 400, 404, 500; S8 additionally shows 409 |
136
+
137
+ S1 schemas distinguish container list summaries from inspection. `ContainerState`
138
+ includes Status, Running, Paused, Restarting, OOMKilled, Dead, Pid, ExitCode,
139
+ Error, StartedAt and FinishedAt. A simulated Pid or rootless/security field is not
140
+ host evidence. An image reference in Config.Image, immutable image identity in
141
+ Image, and container Id must not be conflated. Create returns `Id` and `Warnings`;
142
+ wait returns integer `StatusCode` with optional `Error.Message`.
143
+
144
+ Initial creation scope stores Image, Cmd, Entrypoint, Env, Labels, WorkingDir,
145
+ User, attachment/OpenStdin/StdinOnce/Tty flags, StopSignal/StopTimeout and declared
146
+ HostConfig/NetworkingConfig metadata. Mount/resource/network settings are stored
147
+ observations only. Do not accept unsupported configuration as executed or enforced.
148
+ Resolve any newly supported field's validation against S1 before implementation.
149
+
150
+ ## Lifecycle details that constrain implementation
151
+
152
+ US-005 refresh (2026-09-27): Context7 returned v1.56/current networking and
153
+ entrypoint examples, used only for discovery. Pinned
154
+ [creation](https://github.com/moby/moby/blob/710302ecf2e958db92cb7d92f8838ea063a31765/daemon/create.go)
155
+ and [configuration merge](https://github.com/moby/moby/blob/710302ecf2e958db92cb7d92f8838ea063a31765/daemon/commit.go)
156
+ establish image resolution, platform warnings, request-over-image environment
157
+ and label precedence, command/entrypoint defaults, empty-entrypoint clearing,
158
+ and `no command specified` (400). The selected 1.52 request fields come from S1.
159
+ The emulator stores launch/host/network configuration without executing it. Full
160
+ daemon resource/network validation and image execution are excluded; the README
161
+ lists the explicit supported subset and emulator-only501 behavior. No real Engine
162
+ creation or host resource operation was performed.
163
+
164
+ - S4/S6: a successful stop `204` follows backend completion. For delayed-stop
165
+ scenarios, keep the HTTP operation pending until modeled termination; an admin
166
+ observation that a stop was received is not an early successful vendor response.
167
+ A lost reply or cancellation does not reverse an already accepted stop. This
168
+ preserves US-007's distinction between request acceptance and actual retirement
169
+ without inventing a 202-like Docker acknowledgement.
170
+ - S7: not-running completes immediately when the condition already holds;
171
+ next-exit does not. Removed waits for removal, which also wakes stop waiters.
172
+ S4 sends HTTP 200 headers before the wait result for this API version, then
173
+ writes the eventual JSON result. Tests must distinguish header arrival from
174
+ body completion and cover client cancellation, reset and shutdown.
175
+ - S1: start on an already-running container and stop on an already-stopped
176
+ container use 304, not a fabricated second transition. Kill on a non-running
177
+ container is a conflict. Force removal kills before removal; it is not a
178
+ permission to remove host resources.
179
+ - Before/after-mutation fault points must preserve state/journal/Timeline evidence
180
+ independently of delivery. No source here proves shared-runtime correctness,
181
+ restart/live-restore, or active socket restoration; those are later test gates.
182
+
183
+ ## Attach wire contract and limits
184
+
185
+ For the supported non-TTY upgrade, send `Connection: Upgrade` and `Upgrade: tcp`.
186
+ S4 returns the following header block for v1.52, followed immediately by stream
187
+ bytes (the reader must retain bytes received with the final header fragment):
188
+
189
+ ```http
190
+ HTTP/1.1 101 UPGRADED
191
+ Content-Type: application/vnd.docker.multiplexed-stream
192
+ Connection: Upgrade
193
+ Upgrade: tcp
194
+
195
+ ```
196
+
197
+ D1/S1's old illustrative handshake uses raw-stream, but its framing prose and
198
+ S4 agree on multiplexed-stream for upgraded non-TTY API >=1.42. S4's non-upgrade
199
+ path really uses HTTP 200/raw-stream; it is deliberately excluded from initial
200
+ emulator serving. Fetch cannot represent the raw hijacked duplex connection.
201
+
202
+ Output frames have an 8-byte header: stream byte (1 stdout, 2 stderr), three zero
203
+ bytes, and a uint32 big-endian payload length, followed by exactly that many bytes.
204
+ The spec also describes stream 0 as stdin written to stdout; it is not an input
205
+ framing requirement. S8 wraps output writers only: client stdin is raw bytes,
206
+ enabled only when requested and OpenStdin is true. StdinOnce influences EOF/
207
+ lifetime and needs explicit tests; stdin data must not enter the metadata journal.
208
+ Fragmentation, zero-length payloads, backpressure, EOF and cancellation must be
209
+ tested using an independent consumer. Stream closure alone does not establish
210
+ container termination.
211
+
212
+ Error caveat: S4 handles attach backend errors by hijacking, writing an HTTP error
213
+ with stream content type and a plain-text body, then closing. This differs from
214
+ the specification's generic ErrorResponse schemas. S8 rejects paused/restarting
215
+ containers with conflicts. Implement and test the actual pre-upgrade missing/
216
+ conflict wire errors separately from ordinary JSON REST errors; live oracle
217
+ execution remains necessary to establish observed fidelity.
218
+
219
+ Excluded modes: TTY/PTY, websocket attach, non-upgrade attach, log replay
220
+ (`logs=true`), exec, standalone logs/events, detach-key processing, image
221
+ pull/build, real networking/volumes/__admin/health checks and arbitrary command execution.
222
+ Unsupported modes receive explicit emulator-only 501 errors before upgrade rather
223
+ than silently different successful behavior. Reset/checkout/shutdown terminate
224
+ owned streams and waiters; handles are never serialized into Timeline state.
225
+
226
+ ## Verification gaps and oracle requirements
227
+
228
+ The original US-013 proposal required Engine 29.1.0. The user superseded that
229
+ restriction: use the current selected Engine without downgrading the host.
230
+ US-013 now records an authorized Engine 29.8.0 run below, including actual
231
+ version/API configuration, image identity and cleanup evidence. Engine process restart/live-restore
232
+ needs its own scoped operational authorization and evidence. Harness construction
233
+ alone cannot complete that story.
234
+
235
+ US-004–US-012 must resolve exact filter/name matching, signal validation/error
236
+ wrapping, identifier-prefix ambiguity and every newly implemented field against
237
+ this source pin before advertising support. US-010 must refresh attach research,
238
+ test pre-upgrade errors and supported stdin/EOF semantics. Post-mutation history
239
+ capture and stream invalidation remain implementation hypotheses, not upstream
240
+ guarantees. No SDK is claimed pinned or exercised by this research story.
241
+
242
+
243
+ ### US-006 start/wait implementation evidence
244
+
245
+ Context7 `/docker/docs` was refreshed for start/wait on 2026-09-27. It returned
246
+ current SDK flow examples and older API excerpts, so the pinned sources remain
247
+ authoritative. [Start validation and transitions](https://github.com/moby/moby/blob/710302ecf2e958db92cb7d92f8838ea063a31765/daemon/start.go)
248
+ checks paused before running, returns304 for running/restarting, and409 for
249
+ removal/dead state. S4 rejects start bodies with ContentLength above7 or unknown
250
+ chunked length. S7 preserves FinishedAt across SetRunning, resets ExitCode, and
251
+ notifies all stop waiters at SetStopped; removal also wakes removal-only waiters.
252
+
253
+ The implementation uses explicit admin completion instead of executing a task.
254
+ AutoRemove completes synthetic removal atomically within that synchronous control.
255
+ Wait response headers and completion bodies are separately tested through Fetch
256
+ and Node HTTP; client cancellation, body cancellation, reset, and close release
257
+ transient handles. This is source-backed simulation evidence, not live Engine
258
+ parity. Restart policies, process scheduling and host execution are not simulated.
259
+
260
+
261
+ ### US-007 termination/removal evidence
262
+
263
+ Context7 `/docker/docs` refreshed stop/kill/remove on 2026-09-27: current SDK/CLI
264
+ stop examples, timeout guidance, and historical v1.6/v1.11 operation excerpts.
265
+ Pinned sources resolve the precise v1.52 semantics:
266
+
267
+ - [kill.go](https://github.com/moby/moby/blob/710302ecf2e958db92cb7d92f8838ea063a31765/daemon/kill.go): SIGKILL (including an explicit numeric9) waits for exit; other signals acknowledge delivery. Stopped containers conflict. Linux signal names/numbers are validated before lookup.
268
+ - [delete.go](https://github.com/moby/moby/blob/710302ecf2e958db92cb7d92f8838ea063a31765/daemon/delete.go): concurrent removal conflicts, non-forced running/paused removal conflicts, and force kills before deleting and releasing the name.
269
+ - [signal.go](https://github.com/moby/moby/blob/710302ecf2e958db92cb7d92f8838ea063a31765/vendor/github.com/moby/sys/signal/signal.go) and [Linux map](https://github.com/moby/moby/blob/710302ecf2e958db92cb7d92f8838ea063a31765/vendor/github.com/moby/sys/signal/signal_linux.go): zero is invalid; kill restricts to the Linux map (1–31 and34–64). Real-time aliases are supported.
270
+ - S4/S6 and [HTTP status mapping](https://github.com/moby/moby/blob/710302ecf2e958db92cb7d92f8838ea063a31765/daemon/server/httpstatus/status.go): malformed t returns an unclassified strconv error (500), while a stop signal parsing error is wrapped as System (500). Kill signal errors are InvalidParameter (400). Stop on an already stopped record returns304 before validating its signal.
271
+
272
+ The emulator holds stop/SIGKILL/forced-removal replies until explicit completion;
273
+ acceptance and socket loss never prove retirement. Signal/timeout parameters are
274
+ stored diagnostic metadata; fixture-controlled completion replaces process/timer
275
+ scheduling. v is inert without modeled volumes; link removal is explicit emulator-only501.
276
+ Shared state/checkpoints and transient handles are reused. Fetch and real Node HTTP
277
+ tests cover acceptance versus exit and response loss; no live Engine parity claimed.
278
+
279
+ ## US-009 transport implementation evidence
280
+
281
+ Node's [HTTP API](https://nodejs.org/api/http.html) documents the parser header
282
+ limit, receive timeouts and retained connections. The Docker-local transport uses
283
+ these Node facilities plus its own bounded body collector and owned connection
284
+ set. [Node IPC sockets](https://nodejs.org/api/net.html#ipc-support) define local
285
+ path serving and normal server-close cleanup. Transport bounds and path refusal
286
+ are explicit emulator controls, not assertions of Docker daemon limits. Verification
287
+ uses synthetic project-local Unix sockets and TCP, including a built-entry fixture
288
+ executed under Node; it does not use Linux peer credentials or a real Engine.
289
+
290
+ ## US-010 handshake implementation refresh
291
+
292
+ Context7 `/docker/docs` was refreshed for v1.52 attach on 2026-09-27 and returned
293
+ v1.23/v1.19/v1.11 examples. Re-reading pinned S4/S8 confirmed the existing wire
294
+ contract above; historical raw-stream examples do not supersede non-TTY v1.52
295
+ multiplexed-stream. The Node path now implements admission/headers and plain-text
296
+ backend errors, with synthetic fragmented-write/initial-byte fixtures and an
297
+ independent raw consumer exercised under Bun and Node. Namespace/branch/fault
298
+ selection stays in shared runtime; private response notes record upgrade101
299
+ without a POST mutation checkpoint. Plain Fetch remains unsupported. Full stream
300
+ framing, stdin semantics and history invalidation are US-011; real parity remains
301
+ US-013. These tests do not establish host enforcement or consumer policy.
302
+
303
+ ## US-011 stream framing and lifetime
304
+
305
+ The pinned [stream copier](https://github.com/moby/moby/blob/710302ecf2e958db92cb7d92f8838ea063a31765/daemon/internal/stream/attach.go)
306
+ adds EOF detail to S8: effective stdin copies raw bytes; non-TTY CloseStdin closes
307
+ container input, while the other EOF path closes the attachment's output pipes.
308
+ S8 separately waits for process-not-running for non-TTY StdinOnce. Implemented
309
+ synthetic controls distinguish input EOF, output EOF, cancellation, and explicit
310
+ process completion. Source evidence is not a live-oracle claim. The pinned
311
+ [Linux close notifier](https://github.com/moby/moby/blob/710302ecf2e958db92cb7d92f8838ea063a31765/daemon/server/router/container/notify_linux.go)
312
+ uses EPOLLHUP; portable Node tests assert observed reset/close, not kernel
313
+ provenance. Raw consumers cover fragmented frames, bytes and channel selection,
314
+ input read-ahead/half-close, bounded slow-reader output, peer reset, and instance
315
+ invalidation with stale-handle rejection. Native Node also exercises framing,
316
+ raw input, StdinOnce half-close and process completion.
317
+
318
+ ## US-012 — independent consumer verification
319
+
320
+ The package-owned `test/node-consumer.mjs` imports only the built server entry for
321
+ fixture ownership; its clients use native Node HTTP and raw socket bytes over TCP
322
+ and Unix. All mutations, inspections, setup and completion travel through HTTP;
323
+ Node attachment handles supply scripted output/input observation. Both transports
324
+ exercise accepted create/start/stop/kill/remove response loss and re-inspection,
325
+ retained ordinary HTTP connections and fragmented attach with exact binary frames.
326
+ This is independent client/protocol evidence against the emulator, not a real Engine
327
+ oracle or evidence of the external consumer's retry policy.
328
+
329
+ OpenAPI walks assert eligible planned/exercised operation coverage. A seeded
330
+ nonempty list passes unchanged and fails on a deliberate schema-valid State
331
+ mismatch; the existing ping mismatch check remains. Blocking operations and attach
332
+ retain their deterministic lifecycle/protocol evidence. PRD inspection provenance
333
+ identifies raw-socket Docker consumers; there is no declared SDK consumer, no SDK
334
+ pin exercised here, and no SDK compatibility claim.
335
+
336
+ ## US-013 — current Engine oracle
337
+
338
+ User direction on 2026-09-27 superseded the exact old-Engine prerequisite: use the
339
+ current Engine and do not downgrade the local Docker installation. Desktop 4.92.0
340
+ reports Engine **29.8.0**, Linux/arm64, GitCommit `3ce5872`, API maximum **1.56**,
341
+ minimum **1.40**. The [official release notes](https://docs.docker.com/engine/release-notes/29/)
342
+ list standalone **29.8.1** as the latest patch; this run establishes 29.8.0 evidence
343
+ only. API **1.52** remains the observed consumer contract and comparison subset.
344
+ This separates the API contract from the executable Engine used as an oracle.
345
+
346
+ Refreshed Context7 `/docker/docs` on 2026-09-27. It returned current negotiation
347
+ and release guidance plus historical v1.17 attach excerpts; those excerpts do not
348
+ establish current wire behavior. Resolved `docker-v29.8.0` through its
349
+ [tag object](https://api.github.com/repos/moby/moby/git/tags/dc4db3d292c317ca216dae9301dffc935c8d7680)
350
+ to commit **3ce5872b7950c63ba2ffbc5123101019ff3e6682**, matching the running daemon.
351
+ Compared selected pinned sources against the prior 29.1.0 source:
352
+
353
+ - [Version middleware](https://github.com/moby/moby/blob/3ce5872b7950c63ba2ffbc5123101019ff3e6682/daemon/server/middleware/version.go),
354
+ [daemon attach](https://github.com/moby/moby/blob/3ce5872b7950c63ba2ffbc5123101019ff3e6682/daemon/attach.go), and
355
+ [stream attach](https://github.com/moby/moby/blob/3ce5872b7950c63ba2ffbc5123101019ff3e6682/daemon/internal/stream/attach.go)
356
+ are unchanged between those pins.
357
+ - [Container routes](https://github.com/moby/moby/blob/3ce5872b7950c63ba2ffbc5123101019ff3e6682/daemon/server/router/container/container_routes.go)
358
+ retain the selected attach/wait paths. Changes include invalid-parameter error
359
+ wrapping, legacy capability rejection and API 1.56 Umask handling; the live
360
+ scenarios do not send those fields or malformed parameters.
361
+ - [Stop](https://github.com/moby/moby/blob/3ce5872b7950c63ba2ffbc5123101019ff3e6682/daemon/stop.go)
362
+ and [kill](https://github.com/moby/moby/blob/3ce5872b7950c63ba2ffbc5123101019ff3e6682/daemon/kill.go)
363
+ now use configured daemon default stop timeout and propagate non-cancelable
364
+ contexts. This oracle uses explicit `stop?t=0` and `kill?signal=KILL`; it does not
365
+ establish custom daemon-default timeout equivalence.
366
+ - [Image/config merge](https://github.com/moby/moby/blob/3ce5872b7950c63ba2ffbc5123101019ff3e6682/daemon/commit.go)
367
+ changes map copying without changing the explicit-entrypoint command merge rule.
368
+
369
+ The authorized live run used an explicit Docker Desktop Unix endpoint and existing
370
+ Linux/arm64 image `sha256:dbbd346860d29f1543e991f30f3284bf4ab5f096d049ecc3426528f20b1b6e6b`.
371
+ No image installation, daemon restart or privileged Engine setup was performed.
372
+ Run `mb-oracle-ab4aefb9657d4b6993bc5f5273593796` passed **29 normalized comparisons**:
373
+ create/start/remove statuses; created/running/exited inspection; 101 attach headers;
374
+ raw stdin echo and separated stdout/stderr bytes; exit 7; stop/kill exit 137;
375
+ wait results and post-removal 404. The three named/labeled containers were confirmed
376
+ absent during cleanup. The [captured JSON report](evidence/engine-29.8.0-api-1.52.json)
377
+ retains actual/model values, image identity, exact Engine version and cleanup.
378
+
379
+ An earlier attempt failed at emulator image seeding before creating any container:
380
+ the PostgreSQL image contained unsupported metadata and declared storage. The
381
+ harness now rejects declared volumes/active healthchecks and projects only modeled
382
+ image defaults, recording omitted metadata. The successful fixture omitted
383
+ `ArgsEscaped` and `ExposedPorts` from synthetic image defaults; no ports were
384
+ published and no config-equivalence claim covers those fields.
385
+
386
+ Normalization excludes dynamic IDs/timestamps, daemon identity/platform fields,
387
+ frame/packet boundaries and cross-channel interleaving. The emulator scripts expected
388
+ process completion; it never executes the image. Existing synthetic response-loss
389
+ checks remain separate from real Engine execution. Daemon restart/live-restore,
390
+ host isolation, SDKs, all API 1.56 features and external consumer policies remain
391
+ unverified. The historical emulator discovery profile remains explicit; this narrow
392
+ live run does not certify every operation/response field against every newer Engine.
package/CHANGELOG.md ADDED
@@ -0,0 +1,19 @@
1
+ # Changelog — @emulates/docker
2
+
3
+ ## 1.0.0 (2026-10-07)
4
+
5
+ ### ⚠️ Breaking changes
6
+
7
+ - point the repo at crvouga/emulates ([cdb5e53](https://github.com/crvouga/emulates/commit/cdb5e536ed8c1f52345e3984f089001c25fdb08a))
8
+
9
+ ### Fixes and improvements
10
+
11
+ - format the emulates records query ([8698872](https://github.com/crvouga/emulates/commit/8698872509d06002be5a8fcdc85d79f8462320c3))
12
+
13
+ ### Dependencies
14
+
15
+ - `@emulates/sqlite`
16
+
17
+ ## 0.1.1 (2026-10-06)
18
+
19
+ Initial release.
package/DISCOVERY.md ADDED
@@ -0,0 +1,54 @@
1
+ # @emulates/docker discovery
2
+
3
+ This is the installed-package index for coding agents and tooling. All relative links resolve
4
+ inside `node_modules/@emulates/docker/`; no repository checkout is needed to discover the emulator's
5
+ supported surface or documented behavior.
6
+
7
+ ## Capability and behavior sources
8
+
9
+ | Question | Authoritative file | What it contains |
10
+ | --- | --- | --- |
11
+ | Behaviour and integration | [`README.md`](README.md) | Routes, state transitions, auth, webhooks, controls, presets and deliberate omissions. |
12
+ | Exact capabilities | [`SUPPORT.md`](SUPPORT.md) | Supported, unsupported and parity-covered operations or commands, including reasons for gaps. |
13
+ | Wire contract | [`openapi.yaml`](openapi.yaml) | Machine-readable paths, methods, schemas, responses and parity annotations. |
14
+ | Public API | [`dist/index.d.ts`](dist/index.d.ts) | The installed package's exact TypeScript exports and signatures. |
15
+ | Package metadata | [`package.json`](package.json) | Runtime/entry-point claims, vendor links, parity scope/tier and `emulates.discovery`. |
16
+
17
+ Read these together: the contract/capability matrix says *what* is available, while the README
18
+ defines stateful behavior, lifecycle rules, test controls, and intentional oracle differences.
19
+ If prose and an executable surface disagree, report a parity mismatch instead of adding a
20
+ consumer-side workaround.
21
+
22
+ ## Parity and oracle
23
+
24
+ - Declared parity surface: **Docker Engine container lifecycle, logs and streams**.
25
+ - Oracle: **Live vendor API or sandbox**.
26
+ - Repository command: `bun run parity:service -- docker`.
27
+ - Evidence model: Run from an Emulates checkout; credentials come only from .env.local or GitHub Actions secrets. Missing credentials exit 2.
28
+
29
+ The npm package contains evidence summaries and the exact contract, not credentials or the
30
+ repository-only parity harness. Self-parity/property and acceptance tests run in the Emulates
31
+ repository; live parity is an additional oracle check, not a substitute for the packaged matrix.
32
+
33
+ ## Runtime introspection
34
+
35
+ - `GET /__admin/health`
36
+ - `GET /__admin`
37
+ - `GET /__admin/state`
38
+ - `GET /__admin/requests`
39
+ - `GET /__admin/metrics`
40
+ - `GET /__admin/faults/presets`
41
+ - `GET /__admin/ui`
42
+
43
+ For HTTP services, use `x-emulates-namespace` (or the documented credential/path carrier) so
44
+ parallel tests do not share state. Admin state, journal, metrics and fault-preset endpoints are
45
+ designed for assertions and diagnosis by consuming test suites.
46
+
47
+ ## Report a mismatch or missing capability
48
+
49
+ Follow the [agent reporting contract](https://github.com/crvouga/emulates/blob/main/docs/REPORTING_ISSUES.md). Include package version,
50
+ operation/command, a minimal redacted request, actual emulator result, expected oracle result or vendor
51
+ documentation, and whether the mismatch appears in the matrix. Never include keys, tokens,
52
+ customer data, prompts, PHI, card data, or unredacted recordings.
53
+
54
+ Service key: `docker`.
package/ORACLE.md ADDED
@@ -0,0 +1,85 @@
1
+ # Docker differential oracle
2
+
3
+ This opt-in harness is incomplete evidence until it has run against an explicitly
4
+ authorized Linux Docker Engine supporting the comparison API 1.52. Ordinary tests verify the
5
+ harness against local fixtures; recorded live results are separate evidence.
6
+
7
+ ## Preconditions and scope
8
+
9
+ Supply an explicit Unix socket or loopback HTTP endpoint for a disposable Engine.
10
+ The harness never reads Docker contexts, `DOCKER_HOST`, or a default socket. It
11
+ requires the exact Engine version, an existing immutable Linux image ID, a unique
12
+ run ID, and the `--allow-lifecycle` acknowledgment. The flag records the caller's
13
+ intent; it does not grant agent permission. Obtain operational approval first.
14
+
15
+ The image must contain `/bin/sh`, `read`, `printf`, and `sleep`. Images declaring volumes or active healthchecks are rejected before creation.
16
+ Only modeled image defaults are seeded; omitted metadata fields are recorded.
17
+ No image is pulled,
18
+ built, tagged, or deleted. No volumes, host mounts or published ports are used;
19
+ created containers use `NetworkMode: none`. The harness creates at most three
20
+ containers named `<run-id>-attach`, `<run-id>-stop`, and `<run-id>-kill`, with the
21
+ `emulates.oracle=<run-id>` label. The run ID must be `mb-oracle-` followed by
22
+ 32 lowercase hexadecimal characters; generate a fresh one for each run.
23
+
24
+ Before creating anything, the harness checks the actual Engine version/OS, image
25
+ identity and absence of all three names. It refuses any collision. Each created
26
+ container is started, inspected and removed. Attach executes a shell that echoes
27
+ one input line, writes a stderr marker and exits 7. Stop and kill use a sleeping
28
+ shell loop; stop uses timeout zero and kill uses SIGKILL. Those scenarios expect
29
+ exit 137. These operations affect only these three containers, not the daemon.
30
+
31
+ Cleanup re-inspects each known create intent and requires matching name, image,
32
+ label and (when returned) immutable container ID before force-removing it. It
33
+ never adopts a definitively rejected create. Unknown ownership fails cleanup
34
+ without deleting the object. Cleanup failures make the run fail and identify the
35
+ run-owned name needing investigation. Abrupt process/host termination can prevent
36
+ cleanup; preserve the run ID and inspect ownership before any manual cleanup.
37
+
38
+ ## Invocation
39
+
40
+ Build the package, then run from its directory after obtaining approval:
41
+
42
+ ```sh
43
+ node scripts/docker-oracle.mjs \
44
+ --endpoint unix:///absolute/path/to/approved-engine.sock \
45
+ --engine-version 29.8.0 \
46
+ --image sha256:<64-lowercase-hex-image-id> \
47
+ --run-id mb-oracle-<32-lowercase-hex-run-id> \
48
+ --allow-lifecycle
49
+ ```
50
+
51
+ A loopback endpoint such as `http://127.0.0.1:23750` is also supported. Placeholders
52
+ are rejected. Authenticated/TLS endpoints are outside this harness; use an
53
+ explicitly authorized local endpoint or tunnel. The numeric Engine version must match the endpoint exactly. The selected API
54
+ must be within its advertised minimum/maximum range. Record each new tested
55
+ Engine version; do not require downgrading the user's host.
56
+
57
+ ## Comparison and evidence
58
+
59
+ The JSON report records actual Engine/API/platform information, immutable image,
60
+ normalized comparisons, cleanup results and gaps. Exit status is nonzero on a
61
+ comparison, setup or cleanup failure. Preserve that report under the project's
62
+ ignored `.emulates/` directory, then summarize verified evidence in
63
+ `API_EVIDENCE.md`; do not turn absent execution into a passing result.
64
+
65
+ Comparisons cover create/start/remove HTTP statuses, inspect execution state,
66
+ wait exit code, attach upgrade headers and stdout/stderr bytes. Multiplexed output
67
+ is decoded independently and concatenated by channel, removing arbitrary frame
68
+ and packet boundaries. Output bytes are recorded as base64. Container IDs,
69
+ timestamps, daemon-specific metadata and cross-channel interleaving are not
70
+ compared. The emulator's completion control scripts the known exit behavior; it does
71
+ not execute the image. Tests of image execution or host isolation are not implied.
72
+
73
+ The harness performs no daemon restart or live-restore operation. Existing
74
+ synthetic transport response-loss tests are separate evidence and do not establish
75
+ real restart/live-restore behavior. This harness does not verify an SDK or
76
+ Initiative's recovery policies.
77
+
78
+ ## Recorded run
79
+
80
+ On 2026-09-27, Desktop 4.92.0 / Engine 29.8.0 (Linux/arm64, API range
81
+ 1.40–1.56) passed all 29 comparisons at API 1.52. All three containers were
82
+ confirmed absent afterward. See [the captured report](evidence/engine-29.8.0-api-1.52.json)
83
+ and [source reconciliation](API_EVIDENCE.md#us-013--current-engine-oracle).
84
+ This does not claim testing of standalone Engine 29.8.1, all API 1.56 features,
85
+ or matching daemon identity fields from `/version` and `/info`.