Timing: the "job finished" log line carries the duration of each phase (ack, sandbox start with its sub-steps, guest boot, action resolution, archive misses, run, stop).
Tool cache: the ubuntu-26.04 job image ships Go and Node.js in the runner tool cache (pinned in toolcache.lock), so setup-go and setup-node find them without downloading.
Registry proxy: a Docker Hub pull-through cache on the runner host (internal/registryproxy), reached by the guest dockerd through --registry-mirror on the per-job cache channel. Content is checked by digest, bounded by ERMITE_RUNNER_REGISTRY_CACHE_MAX (default 10G, 0 turns it off), with per-job and global download limits.
Checks: go vet, go test -race ./..., staticcheck, the live Docker Hub test (ERMITE_RUNNER_IT_REGISTRY=1), and an end-to-end run on a local stack in vm mode (setup-go served from the tool cache; second pull of alpine:3 served entirely from the proxy).
Not verified: the e2e suite after the last refactor commit, an arm64 image, container mode under rootless Docker.
The runner serves the GitHub Actions cache and artifacts v4 to jobs. Both use one channel per sandbox.
3
+
The runner serves the GitHub Actions cache, artifacts v4 and a Docker Hub mirror to jobs. All three use one channel per sandbox.
4
4
5
5
## Channel
6
6
@@ -79,3 +79,58 @@ Works with `actions/cache`, `setup-go` and `setup-node` with `cache:`, and anyth
79
79
- Artifacts v3.
80
80
- The `artifact-url` output. It points at the fake host. The run page in ermite lists the artifacts.
81
81
- Downloads from another run (`run-id` with `github-token`).
82
+
83
+
## Docker Hub images
84
+
85
+
The runner provides an anonymous, pull-only Docker Hub mirror on the same private
86
+
channel. Each job's dockerd uses `http://127.0.0.1:9089` as a registry mirror.
87
+
Other registries are not cached. There is no HTTPS interception, and the mirror
88
+
needs no network policy rule. Guest credentials are never forwarded upstream. Private pulls
89
+
fall back to Docker Hub directly, where dockerd can use the job's credentials.
90
+
91
+
Verified sha256 blobs and manifests live under `<state dir>/registry/`. Tags are
92
+
revalidated by an upstream HEAD on each request, with the client's Accept headers
93
+
reduced to the known manifest media types. If Docker Hub is unreachable, times
94
+
out, returns a server error or rate-limits (429), a previously cached tag can be
95
+
served stale while its manifest remains stored. Unknown tags return an error so
96
+
dockerd can fall back.
97
+
98
+
A cold blob is downloaded once, whoever asks for it, and is not tied to any one
99
+
client: every client waits for the digest check and then reads the stored file.
100
+
A corrupt download returns 502 and leaves no file. A client that disconnects
101
+
does not stop the download while another client waits for it; once no client
102
+
has waited for 5 s, the download is cancelled and its temporary file removed.
103
+
Each job may have started at most 3 cold blob downloads at once (dockerd's
104
+
default concurrency), and at most 16 run at once across all jobs. A download
105
+
counts against the job that started it until it ends; a job that asks for a
106
+
layer another job is already downloading waits for it and uses none of its
107
+
quota. A request over either limit waits up to 30 s for a slot, its connection
108
+
kept alive, and then gets 503. A refused layer makes dockerd retry for about
109
+
50 s before it pulls that layer from Docker Hub directly. Each sandbox may also
110
+
have at most 8 registry requests in progress; one more gets 503 at once.
111
+
112
+
Downloads have no total time limit, but they fail after 30 s without progress.
113
+
Auth, manifest and HEAD requests are limited to 10 s. Auth and manifest requests
114
+
never follow redirects; blob requests, HEAD included, follow the CDN redirect
115
+
without the bearer token. Stored blobs support range requests. Cold blob HEAD
116
+
requests fetch only upstream metadata, not the layer body.
117
+
118
+
`ERMITE_RUNNER_REGISTRY_CACHE_MAX` defaults to `10G`. Blobs, manifests and tag
119
+
files share one least-recently-used eviction; each is charged at least 4 KiB, so
120
+
the number of files stays below the cap divided by 4 KiB. An entry that is being served is
121
+
never evicted. This cap is independent of
122
+
`ERMITE_RUNNER_CACHE_MAX`. A zero size in any unit (`0`, `0M`, `0G`) disables only
123
+
the proxy. `ERMITE_RUNNER_CACHE=0` disables the whole channel and therefore also
124
+
the proxy (logged at startup). Dockerd retains its mirror setting and falls back
125
+
to Docker Hub if the mirror is unavailable. To clear the cache, stop the runner
126
+
and remove `<state dir>/registry/`.
127
+
128
+
Jobs with neither cache permissions nor artifacts get the loopback channel for
129
+
dockerd when the proxy is on, but no `ACTIONS_CACHE_URL`, `ACTIONS_RESULTS_URL`, or
130
+
`ACTIONS_RUNTIME_TOKEN` is exposed to their steps. Registry routes need no token:
131
+
only public content is served over a channel exclusive to that sandbox.
132
+
133
+
The host logs one `registry manifest` line per request with `job_id`, `repo`,
134
+
`ref`, `hit` and `ms`. Blob misses log `registry blob` at debug level with
135
+
`job_id`, `digest`, `bytes` and `ms`; `job_id` is the job that started the
136
+
download.
@@ -1,6 +1,6 @@
1
# Cache and artifacts
1
# Cache and artifacts
2
2
3
-
The runner serves the GitHub Actions cache and artifacts v4 to jobs. Both use one channel per sandbox.
3
+
The runner serves the GitHub Actions cache, artifacts v4 and a Docker Hub mirror to jobs. All three use one channel per sandbox.
4
4
5
## Channel
5
## Channel
6
6
@@ -79,3 +79,58 @@ Works with `actions/cache`, `setup-go` and `setup-node` with `cache:`, and anyth
79
- Artifacts v3.
79
- Artifacts v3.
80
- The `artifact-url` output. It points at the fake host. The run page in ermite lists the artifacts.
80
- The `artifact-url` output. It points at the fake host. The run page in ermite lists the artifacts.
81
- Downloads from another run (`run-id` with `github-token`).
81
- Downloads from another run (`run-id` with `github-token`).
82
+
83
+
## Docker Hub images
84
+
85
+
The runner provides an anonymous, pull-only Docker Hub mirror on the same private
86
+
channel. Each job's dockerd uses `http://127.0.0.1:9089` as a registry mirror.
87
+
Other registries are not cached. There is no HTTPS interception, and the mirror
88
+
needs no network policy rule. Guest credentials are never forwarded upstream. Private pulls
89
+
fall back to Docker Hub directly, where dockerd can use the job's credentials.
90
+
91
+
Verified sha256 blobs and manifests live under `<state dir>/registry/`. Tags are
92
+
revalidated by an upstream HEAD on each request, with the client's Accept headers
93
+
reduced to the known manifest media types. If Docker Hub is unreachable, times
94
+
out, returns a server error or rate-limits (429), a previously cached tag can be
95
+
served stale while its manifest remains stored. Unknown tags return an error so
96
+
dockerd can fall back.
97
+
98
+
A cold blob is downloaded once, whoever asks for it, and is not tied to any one
99
+
client: every client waits for the digest check and then reads the stored file.
100
+
A corrupt download returns 502 and leaves no file. A client that disconnects
101
+
does not stop the download while another client waits for it; once no client
102
+
has waited for 5 s, the download is cancelled and its temporary file removed.
103
+
Each job may have started at most 3 cold blob downloads at once (dockerd's
104
+
default concurrency), and at most 16 run at once across all jobs. A download
105
+
counts against the job that started it until it ends; a job that asks for a
106
+
layer another job is already downloading waits for it and uses none of its
107
+
quota. A request over either limit waits up to 30 s for a slot, its connection
108
+
kept alive, and then gets 503. A refused layer makes dockerd retry for about
109
+
50 s before it pulls that layer from Docker Hub directly. Each sandbox may also
110
+
have at most 8 registry requests in progress; one more gets 503 at once.
111
+
112
+
Downloads have no total time limit, but they fail after 30 s without progress.
113
+
Auth, manifest and HEAD requests are limited to 10 s. Auth and manifest requests
114
+
never follow redirects; blob requests, HEAD included, follow the CDN redirect
115
+
without the bearer token. Stored blobs support range requests. Cold blob HEAD
116
+
requests fetch only upstream metadata, not the layer body.
117
+
118
+
`ERMITE_RUNNER_REGISTRY_CACHE_MAX` defaults to `10G`. Blobs, manifests and tag
119
+
files share one least-recently-used eviction; each is charged at least 4 KiB, so
120
+
the number of files stays below the cap divided by 4 KiB. An entry that is being served is
121
+
never evicted. This cap is independent of
122
+
`ERMITE_RUNNER_CACHE_MAX`. A zero size in any unit (`0`, `0M`, `0G`) disables only
123
+
the proxy. `ERMITE_RUNNER_CACHE=0` disables the whole channel and therefore also
124
+
the proxy (logged at startup). Dockerd retains its mirror setting and falls back
125
+
to Docker Hub if the mirror is unavailable. To clear the cache, stop the runner
126
+
and remove `<state dir>/registry/`.
127
+
128
+
Jobs with neither cache permissions nor artifacts get the loopback channel for
129
+
dockerd when the proxy is on, but no `ACTIONS_CACHE_URL`, `ACTIONS_RESULTS_URL`, or
130
+
`ACTIONS_RUNTIME_TOKEN` is exposed to their steps. Registry routes need no token:
131
+
only public content is served over a channel exclusive to that sandbox.
132
+
133
+
The host logs one `registry manifest` line per request with `job_id`, `repo`,
134
+
`ref`, `hit` and `ms`. Blob misses log `registry blob` at debug level with
135
+
`job_id`, `digest`, `bytes` and `ms`; `job_id` is the job that started the
136
+
download.
Mdocs/configuration.md+2-1
@@ -74,9 +74,10 @@ The token has the scope `runner:register`. It can read the code and the secrets
74
74
75
75
| Variable | Default | Meaning |
76
76
| --- | --- | --- |
77
-
| `ERMITE_RUNNER_CACHE` | `1` | `0` turns the Actions cache off. See [cache and artifacts](cache-and-artifacts.md) |
77
+
| `ERMITE_RUNNER_CACHE` | `1` | `0` turns the cache channel off, including the Actions cache and Docker Hub proxy. See [cache and artifacts](cache-and-artifacts.md) |
78
78
| `ERMITE_RUNNER_CACHE_MAX` | `10G` | Size cap of the cache. Least recently used entries are evicted |
79
79
| `ERMITE_RUNNER_ACTIONS_CACHE_MAX` | `2G` | Size cap of the action archive cache. `0` turns it off |
80
+
| `ERMITE_RUNNER_REGISTRY_CACHE_MAX` | `10G` | Docker Hub blob and manifest cache cap, independent of `ERMITE_RUNNER_CACHE_MAX`. A zero size (`0`, `0M`, `0G`) turns only the proxy off; `ERMITE_RUNNER_CACHE=0` also disables it |
80
81
| `ERMITE_RUNNER_ARTIFACTS` | `1` | `0` turns artifacts v4 off. Artifacts need the cache service |
81
82
| `ERMITE_RUNNER_NODE` | `1` | `0` turns the Node.js externals off. See [externals](externals.md) |
82
83
| `ERMITE_RUNNER_GUEST_FORGE` | `0` | `1` lets sandboxes reach the server host and port. Read the security notes first. See [sandboxes](sandboxes.md#guest-forge-access) |
@@ -74,9 +74,10 @@ The token has the scope `runner:register`. It can read the code and the secrets
74
74
75
| Variable | Default | Meaning |
75
| Variable | Default | Meaning |
76
| --- | --- | --- |
76
| --- | --- | --- |
77
-
| `ERMITE_RUNNER_CACHE` | `1` | `0` turns the Actions cache off. See [cache and artifacts](cache-and-artifacts.md) |
77
+
| `ERMITE_RUNNER_CACHE` | `1` | `0` turns the cache channel off, including the Actions cache and Docker Hub proxy. See [cache and artifacts](cache-and-artifacts.md) |
78
| `ERMITE_RUNNER_CACHE_MAX` | `10G` | Size cap of the cache. Least recently used entries are evicted |
78
| `ERMITE_RUNNER_CACHE_MAX` | `10G` | Size cap of the cache. Least recently used entries are evicted |
79
| `ERMITE_RUNNER_ACTIONS_CACHE_MAX` | `2G` | Size cap of the action archive cache. `0` turns it off |
79
| `ERMITE_RUNNER_ACTIONS_CACHE_MAX` | `2G` | Size cap of the action archive cache. `0` turns it off |
80
+
| `ERMITE_RUNNER_REGISTRY_CACHE_MAX` | `10G` | Docker Hub blob and manifest cache cap, independent of `ERMITE_RUNNER_CACHE_MAX`. A zero size (`0`, `0M`, `0G`) turns only the proxy off; `ERMITE_RUNNER_CACHE=0` also disables it |
80
| `ERMITE_RUNNER_ARTIFACTS` | `1` | `0` turns artifacts v4 off. Artifacts need the cache service |
81
| `ERMITE_RUNNER_ARTIFACTS` | `1` | `0` turns artifacts v4 off. Artifacts need the cache service |
81
| `ERMITE_RUNNER_NODE` | `1` | `0` turns the Node.js externals off. See [externals](externals.md) |
82
| `ERMITE_RUNNER_NODE` | `1` | `0` turns the Node.js externals off. See [externals](externals.md) |
82
| `ERMITE_RUNNER_GUEST_FORGE` | `0` | `1` lets sandboxes reach the server host and port. Read the security notes first. See [sandboxes](sandboxes.md#guest-forge-access) |
83
| `ERMITE_RUNNER_GUEST_FORGE` | `0` | `1` lets sandboxes reach the server host and port. Read the security notes first. See [sandboxes](sandboxes.md#guest-forge-access) |
Mdocs/development.md+26-0
@@ -23,6 +23,7 @@ They are opt-in. They run the real stub executor. Output and exit codes come bac
23
23
| `ERMITE_RUNNER_IT_E2E=1` | End-to-end job tests in `internal/e2e` |
24
24
| `ERMITE_RUNNER_IT_NET=1` | Packet-level firewall test |
25
25
| `ERMITE_RUNNER_IT_DOCKER_API=1` | Docker Engine calls against any daemon, including rootless |
26
+
| `ERMITE_RUNNER_IT_REGISTRY=1` | Live Docker Hub pulls through the registry proxy with `curl` (`TestDockerHubCurl`). Needs internet access |
26
27
| `ERMITE_RUNNER_IT_NODE_DIR` | Directory with `node-v24.21.0-linux-x64.tar.gz`, for offline hosts |
27
28
| `ERMITE_RUNNER_TEST_IMAGE` | Image used by the container and end-to-end tests |
28
29
@@ -63,3 +64,28 @@ ERMITE_RUNNER_IT_E2E=1 ERMITE_RUNNER_TEST_IMAGE=ermite-runner-ubuntu-26.04:dev G
63
64
- **Docker API.** Production `container` mode supports Docker Engine and sysbox. Podman's compatibility API is not a drop-in: it cannot disable DNS on the network that the runner creates.
64
65
- **Jailer.** The jailer needs `--chroot-base-dir` to exist. The driver creates `<state dir>/jail`. It leaves `<state dir>/jail/<exec name>` and `/sys/fs/cgroup/firecracker` behind.
65
66
- **Host firewalls.** A firewall that rejects `53` on `ertap*` or `erbr*` breaks guest DNS. The symptom is `dns silent` in `TestVMNetworkPolicy`.
67
+
68
+
## Measuring start-up on a small host
69
+
70
+
Build and register the [runner container](container.md) as usual, then constrain
71
+
its CPU budget to reproduce a small host without changing the boot sequence:
Compare `sandbox_ms`, the `sb_*` stages and `guest_*` boot stages across jobs.
90
+
`run_ms` includes action downloads and step execution; archive misses have their
91
+
own counters. See [Job timing](service.md#job-timing) for the full field table.
@@ -23,6 +23,7 @@ They are opt-in. They run the real stub executor. Output and exit codes come bac
23
| `ERMITE_RUNNER_IT_E2E=1` | End-to-end job tests in `internal/e2e` |
23
| `ERMITE_RUNNER_IT_E2E=1` | End-to-end job tests in `internal/e2e` |
24
| `ERMITE_RUNNER_IT_NET=1` | Packet-level firewall test |
24
| `ERMITE_RUNNER_IT_NET=1` | Packet-level firewall test |
25
| `ERMITE_RUNNER_IT_DOCKER_API=1` | Docker Engine calls against any daemon, including rootless |
25
| `ERMITE_RUNNER_IT_DOCKER_API=1` | Docker Engine calls against any daemon, including rootless |
26
+
| `ERMITE_RUNNER_IT_REGISTRY=1` | Live Docker Hub pulls through the registry proxy with `curl` (`TestDockerHubCurl`). Needs internet access |
26
| `ERMITE_RUNNER_IT_NODE_DIR` | Directory with `node-v24.21.0-linux-x64.tar.gz`, for offline hosts |
27
| `ERMITE_RUNNER_IT_NODE_DIR` | Directory with `node-v24.21.0-linux-x64.tar.gz`, for offline hosts |
27
| `ERMITE_RUNNER_TEST_IMAGE` | Image used by the container and end-to-end tests |
28
| `ERMITE_RUNNER_TEST_IMAGE` | Image used by the container and end-to-end tests |
28
29
@@ -63,3 +64,28 @@ ERMITE_RUNNER_IT_E2E=1 ERMITE_RUNNER_TEST_IMAGE=ermite-runner-ubuntu-26.04:dev G
63
- **Docker API.** Production `container` mode supports Docker Engine and sysbox. Podman's compatibility API is not a drop-in: it cannot disable DNS on the network that the runner creates.
64
- **Docker API.** Production `container` mode supports Docker Engine and sysbox. Podman's compatibility API is not a drop-in: it cannot disable DNS on the network that the runner creates.
64
- **Jailer.** The jailer needs `--chroot-base-dir` to exist. The driver creates `<state dir>/jail`. It leaves `<state dir>/jail/<exec name>` and `/sys/fs/cgroup/firecracker` behind.
65
- **Jailer.** The jailer needs `--chroot-base-dir` to exist. The driver creates `<state dir>/jail`. It leaves `<state dir>/jail/<exec name>` and `/sys/fs/cgroup/firecracker` behind.
65
- **Host firewalls.** A firewall that rejects `53` on `ertap*` or `erbr*` breaks guest DNS. The symptom is `dns silent` in `TestVMNetworkPolicy`.
66
- **Host firewalls.** A firewall that rejects `53` on `ertap*` or `erbr*` breaks guest DNS. The symptom is `dns silent` in `TestVMNetworkPolicy`.
67
+
68
+
## Measuring start-up on a small host
69
+
70
+
Build and register the [runner container](container.md) as usual, then constrain
71
+
its CPU budget to reproduce a small host without changing the boot sequence:
| `vm` | Directory | `vmlinux` (`Image` on arm64) and `base.ext4`. Default `<state dir>/image` |
9
9
10
-
Both come from `images/ubuntu-26.04/Dockerfile` (Ubuntu 26.04 LTS). The runner executable and Node.js are attached by the host. They are never part of the image.
10
+
Both come from `images/ubuntu-26.04/Dockerfile` (Ubuntu 26.04 LTS). The runner executable and action runtime Node.js are attached by the host. The image also includes system Node.js and the cached toolchains listed below.
11
11
12
12
## Build
13
13
@@ -26,9 +26,24 @@ scripts/build-image.sh tmp/image # builds the OCI image and tmp/image/base.ex
26
26
## What the image must contain
27
27
28
28
- User `runner`, uid and gid 1001, home `/home/runner`. Steps run as this user. Without it, steps run as root (test images only).
29
-
-`/home/runner/work` and `/opt/hostedtoolcache`, owned by `runner`.
29
+
-`/home/runner/work` and `/opt/hostedtoolcache`, owned by `runner`. The tool cache holds the toolchains listed in `images/ubuntu-26.04/toolcache.lock` for the image architecture.
30
30
-`bash` and `sh`. `python3` and a `python` command for `shell: python`.
31
31
-`dockerd` and passwordless `sudo` for `runner`, for jobs that use Docker.
32
+
- No `registry-mirrors` in `/etc/docker/daemon.json`. The runner starts dockerd with `--registry-mirror` (its Docker Hub mirror), and dockerd refuses to start when an option is set both as a flag and in that file.
32
33
- Common tools: git, curl, jq, build-essential, ca-certificates, tzdata, zip and unzip.
33
34
34
35
`pwsh` is not in the image. A step that asks for it fails with `pwsh: command not found`.
36
+
37
+
## Toolchains
38
+
39
+
`images/ubuntu-26.04/toolcache.lock` pins Go and Node.js archives by version,
40
+
action architecture (`x64` or `arm64`), official SHA-256 and download URL.
41
+
To add a version, add a line for each supported architecture with the checksum
42
+
from go.dev's download JSON or nodejs.org's `SHASUMS256.txt`, then rebuild with
43
+
`scripts/build-image.sh tmp/image`. A checksum mismatch fails the build.
44
+
45
+
The installer uses the `@actions/tool-cache` layout, including the sibling
46
+
`<arch>.complete` marker. `actions/setup-go` with an exact version, including
47
+
one read from `go.mod`, uses the cache only for an exact match. A range such as
48
+
`1.27` selects the highest matching cached version. Versions not in the cache
49
+
still download normally (and `check-latest: true` may check upstream).
@@ -7,7 +7,7 @@ A job runs in the image named by `ERMITE_RUNNER_IMAGE`.
| `vm` | Directory | `vmlinux` (`Image` on arm64) and `base.ext4`. Default `<state dir>/image` |
8
| `vm` | Directory | `vmlinux` (`Image` on arm64) and `base.ext4`. Default `<state dir>/image` |
9
9
10
-
Both come from `images/ubuntu-26.04/Dockerfile` (Ubuntu 26.04 LTS). The runner executable and Node.js are attached by the host. They are never part of the image.
10
+
Both come from `images/ubuntu-26.04/Dockerfile` (Ubuntu 26.04 LTS). The runner executable and action runtime Node.js are attached by the host. The image also includes system Node.js and the cached toolchains listed below.
11
11
12
## Build
12
## Build
13
13
@@ -26,9 +26,24 @@ scripts/build-image.sh tmp/image # builds the OCI image and tmp/image/base.ex
26
## What the image must contain
26
## What the image must contain
27
27
28
- User `runner`, uid and gid 1001, home `/home/runner`. Steps run as this user. Without it, steps run as root (test images only).
28
- User `runner`, uid and gid 1001, home `/home/runner`. Steps run as this user. Without it, steps run as root (test images only).
29
-
-`/home/runner/work` and `/opt/hostedtoolcache`, owned by `runner`.
29
+
-`/home/runner/work` and `/opt/hostedtoolcache`, owned by `runner`. The tool cache holds the toolchains listed in `images/ubuntu-26.04/toolcache.lock` for the image architecture.
30
-`bash` and `sh`. `python3` and a `python` command for `shell: python`.
30
-`bash` and `sh`. `python3` and a `python` command for `shell: python`.
31
-`dockerd` and passwordless `sudo` for `runner`, for jobs that use Docker.
31
-`dockerd` and passwordless `sudo` for `runner`, for jobs that use Docker.
32
+
- No `registry-mirrors` in `/etc/docker/daemon.json`. The runner starts dockerd with `--registry-mirror` (its Docker Hub mirror), and dockerd refuses to start when an option is set both as a flag and in that file.
32
- Common tools: git, curl, jq, build-essential, ca-certificates, tzdata, zip and unzip.
33
- Common tools: git, curl, jq, build-essential, ca-certificates, tzdata, zip and unzip.
33
34
34
`pwsh` is not in the image. A step that asks for it fails with `pwsh: command not found`.
35
`pwsh` is not in the image. A step that asks for it fails with `pwsh: command not found`.
36
+
37
+
## Toolchains
38
+
39
+
`images/ubuntu-26.04/toolcache.lock` pins Go and Node.js archives by version,
40
+
action architecture (`x64` or `arm64`), official SHA-256 and download URL.
41
+
To add a version, add a line for each supported architecture with the checksum
42
+
from go.dev's download JSON or nodejs.org's `SHASUMS256.txt`, then rebuild with
43
+
`scripts/build-image.sh tmp/image`. A checksum mismatch fails the build.
44
+
45
+
The installer uses the `@actions/tool-cache` layout, including the sibling
46
+
`<arch>.complete` marker. `actions/setup-go` with an exact version, including
47
+
one read from `go.mod`, uses the cache only for an exact match. A range such as
48
+
`1.27` selects the highest matching cached version. Versions not in the cache
49
+
still download normally (and `check-latest: true` may check upstream).
Mdocs/sandboxes.md+0-1
@@ -78,7 +78,6 @@ From a sandbox:
78
78
79
79
- Source address and destination checks run before any "established" accept. A guest cannot forge another's flow.
80
80
- The DNS forwarder allows 32 in-flight queries per listener (extra UDP is dropped), 16 TCP connections, and 30 s per TCP connection.
81
-
- Upstream queries use UDP for 1 s, then TCP on the same resolver (also when UDP is truncated). One query takes at most 6 s. A UDP answer larger than the client accepts (512 bytes without EDNS) is replaced by an empty reply with TC=1, so the client retries over TCP.
82
81
- Golden rulesets: `internal/netpolicy/testdata`.
83
82
84
83
Host side effects:
@@ -78,7 +78,6 @@ From a sandbox:
78
78
79
- Source address and destination checks run before any "established" accept. A guest cannot forge another's flow.
79
- Source address and destination checks run before any "established" accept. A guest cannot forge another's flow.
80
- The DNS forwarder allows 32 in-flight queries per listener (extra UDP is dropped), 16 TCP connections, and 30 s per TCP connection.
80
- The DNS forwarder allows 32 in-flight queries per listener (extra UDP is dropped), 16 TCP connections, and 30 s per TCP connection.
81
-
- Upstream queries use UDP for 1 s, then TCP on the same resolver (also when UDP is truncated). One query takes at most 6 s. A UDP answer larger than the client accepts (512 bytes without EDNS) is replaced by an empty reply with TC=1, so the client retries over TCP.
82
- Golden rulesets: `internal/netpolicy/testdata`.
81
- Golden rulesets: `internal/netpolicy/testdata`.
83
82
84
Host side effects:
83
Host side effects:
Mdocs/service.md+31-0
@@ -64,3 +64,34 @@ Do not add:
64
64
-`Delegate` and `UMask`: not needed.
65
65
66
66
The security boundary of a job is its microVM or container, not the unit.
67
+
68
+
## Job timing
69
+
70
+
The `job finished` line carries integer milliseconds (all `*_ms` fields).
71
+
Phases that did not run are omitted, and the driver (`sb_*`) timings appear only
72
+
when the sandbox started. `resolve_ms` includes `archive_miss_ms`, and both are
73
+
part of `run_ms`.
74
+
75
+
| Fields | Meaning |
76
+
| --- | --- |
77
+
| `ack_ms` | Assignment received to successful first heartbeat acknowledgement |
78
+
| `sandbox_ms` | Host `startSandbox` call |
79
+
| `run_ms` | Executor session (`drive`) |
80
+
| `stop_ms` | Sandbox `Stop` call |
81
+
| `complete_ms` | Completion call, including retries |
82
+
| `total_ms` | Assignment received to this log line, including log flushing |
83
+
| `resolve_calls`, `resolve_ms` | Number and summed duration of action resolution requests |
84
+
| `archive_misses`, `archive_miss_ms`, `archive_miss_bytes` | Archive misses and their summed fetch duration and bytes |
85
+
| `sb_tap_ms`, `sb_net_ms` (VM) | TAP creation and the rest of network setup |
E2E on the head of this MR (2b9f5c9), production setup: unprivileged container under rootless Docker, vm isolation, /dev/kvm + /dev/net/tun + NET_ADMIN only, guest forge relay, the env of ermite-prod.toml. Both jobs green on two passes. setup-go: Found in cache (1.27.1). alpine:3: first pass fetched through the proxy, second pass all manifests hit=true and no blob fetched. Job total 2.6 s (container) and 6.2 s (go) on the warm pass. Not covered: rootless Podman itself (no podman on the test host).