Skip to content

Make the supervisor work on Podman: reach the socket, and wait for healthy - #372

Merged
davidmckayv merged 2 commits into
mainfrom
podman-supervisor
Sep 4, 2026
Merged

Make the supervisor work on Podman: reach the socket, and wait for healthy#372
davidmckayv merged 2 commits into
mainfrom
podman-supervisor

Conversation

@davidmckayv

Copy link
Copy Markdown
Contributor

S1 of the OpenBot Desktop build: prove the stack on Podman. It did not work. Two defects, both fixed here.

1. The supervisor could not reach the engine

The supervisor could not reach Docker (Error: Was there a typo in the url or port?). A computer cannot be started without it.

Not a path problem. Podman's VM already symlinks /var/run/docker.sock to the rootless socket, so the existing compose mount is correct and needs no change. The VM runs SELinux enforcing and labels the socket user_tmp_t:

srw-rw----. 1 core core unconfined_u:object_r:user_tmp_t:s0 /run/user/501/podman/podman.sock

security_opt: [label=disable] on the supervisor is the whole fix. No-op on Docker Desktop; understood by Docker on SELinux hosts.

2. Every cold start of a computer raced the first request

After the socket worked, the supervisor created the computer and the request still failed, now with The assistant's computer is not running.

The computer was running, and answering /health with 200. What was missing was the health status: Podman reports no HEALTHCHECK. Its images are OCI-manifest, the OCI image config has no healthcheck field, and the instruction is dropped both when Podman builds an image and when it pulls one that has it.

Read straight from the registry, the published image has it:

"Healthcheck": { "Test": ["CMD-SHELL", "bun -e \"...\/health...\""], "Interval": 2000000000, ... }

podman inspect of that same pulled image: NONE.

With no health to read, waitUntilAnswering took the branch that accepts a merely-running container. A running container is not a Chromium that is answering, so the first request hit a port nothing was listening on. That branch's own comment called this out as the worse-but-necessary option; on Podman it was not the exception, it was every time.

The supervisor now passes the healthcheck when it creates the computer instead of inheriting it. Readiness stops depending on how the image was built. Podman honours an explicit healthcheck: it is how every service in docker-compose.yml reports healthy there. Docker is unchanged, because the numbers are the ones agent-computer/Dockerfile already declares.

This one collides with 0.0.7's zstd images

zstd layers require OCI media types. An OCI image cannot carry a HEALTHCHECK that Podman will report. So zstd and image-declared readiness could not both be had while readiness was read off the image. Stating it at create time is what makes them independent.

Verified on Podman, not reasoned about

Rootless Podman 6.1.1, applehv, macOS arm64. Isolated stack: own Compose project, COMPUTER_NAMESPACE, database and ports.

before after
cold start fail, twice 5 pass, 0 fail
created computer's health <none> healthy
image's own healthcheck NONE NONE, still

That last row is the point: the computer reports healthy while the image it came from carries nothing, so it is the explicit healthcheck doing the work.

The full journey passes on Podman: compose up, supervisor holding the rootless socket, a per-Bot computer created through it, and a harness answering a live AG-UI run through the gateway with the trail recording it.

format:check, lint and the supervisor unit tests are clean. supervisor typecheck reports the same five pre-existing TS7006 errors as main, byte-identical apart from shifted line numbers; this change adds none.

Three doc corrections this also produced

Not in this PR, they are build-doc edits:

  • --provider applehv is a no-op on 6.1.1: it is the default on Apple silicon. The pin was written against 5.7.x when libkrun was the default.
  • No socket remapping is needed. Podman supplies the /var/run/docker.sock symlink in the VM.
  • Dynamic port forwarding works: gvproxy forwarded the computer's published port to macOS, 200 from the host, so the host-native core reaching computers over loopback holds on Podman.

…althy

Nothing had run this stack on Podman. Two things stopped it, both found by
bringing it up on rootless Podman 6.1.1 on macOS and running the smoke journey.

The supervisor could not reach the engine. The socket is where it expects it,
and the mount is correct: Podman's virtual machine already symlinks
/var/run/docker.sock to the rootless socket, so no path changes. The machine
runs SELinux enforcing and labels that socket user_tmp_t, which a container
without label=disable cannot read. Declaring it is the whole fix and is a no-op
on Docker.

Then every cold start of a computer raced the first request to it. Readiness
was read off the image's HEALTHCHECK, and Podman reports none: its images are
OCI-manifest, the OCI image config has no healthcheck field, and the
instruction is dropped both when Podman builds an image and when it pulls one
that carries it. The published agent-computer config does carry it; podman
inspect of that same image reports none. With nothing to read, the supervisor
took the branch that accepts a container which is merely running, and a running
container is not a Chromium that is answering, so the request arrived at a port
nothing was listening on yet and was reported as a computer that is not
running.

It now states the healthcheck when it creates the computer rather than
inheriting it, so readiness stops depending on how the image was built. Podman
honours an explicit healthcheck: it is how every compose service reports
healthy there. Docker is unchanged, because the numbers are the ones the
Dockerfile already declared.

This also matters for the zstd images published in 0.0.7. zstd layers require
OCI media types, and an OCI image cannot carry a HEALTHCHECK that Podman will
report, so the two could not both be had while readiness was read off the
image.

Verified on Podman: cold start failed before the change, twice, and passes
after, with the created computer reporting healthy while the image it came from
reports no healthcheck at all.
`as const` made every field readonly, and `HealthConfig` declares `Test` as a
mutable `string[]`, so the object could not be assigned to it. Caught by
`types (supervisor)`, which runs `tsc` from that package after installing its
own dependencies; running the script from the repository root instead reports a
different and misleading set of errors, because the package's own
`node_modules` is not there.
@davidmckayv davidmckayv added the full-ci Run the slow image builds on this PR label Sep 4, 2026
@davidmckayv
davidmckayv merged commit e8197d9 into main Sep 4, 2026
14 checks passed
@davidmckayv
davidmckayv deleted the podman-supervisor branch September 4, 2026 20:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

full-ci Run the slow image builds on this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants