feat: the failure-injection levers get an HTTP surface - #66
Merged
Conversation
m80 could force a build to FAILED and a connector to any of its seven reason codes, and only from Go. A test that imported m80 could drive them; a suite pointed at ghcr.io/intentius/m80 could not reach them at all — exactly backwards, since a container is what a consumer tests against. A KubeMicroVM test wanting to watch its operator handle a failed image build had no way to cause one. POST /_m80/inject arms either lever. Three things about the shape are deliberate. Keyed by name, not the ARN the issue sketched. A lever arms before the resource exists — that is what "the next build of this image fails" means — so at the moment of arming there is no ARN to name it with. The response carries "injected": true. A state m80 reached under its own rules never carries it, so a consumer asserting on that field cannot mistake an injected FAILED for a real one. That was the issue's first requirement and it is the reason the endpoint returns a body at all rather than a bare 200. Off unless -enable-injection. Nothing under /_m80/ is signed, so anything that can reach the port can arm a lever; the flag is the consent, same posture -serve-sts takes. Without it the route is still registered and answers 404 naming the flag — a bare 404 is indistinguishable from a typo in the path, and the issue's second requirement was that the levers not be reachable by accident, not that they be undiscoverable. api.Server grows a Handle method for this. Operations still go through Register, which panics on an unknown one, so nothing outside the Routes table can claim an operation's path. Verified against the container rather than in-process: armed a build over HTTP, created the image with the AWS CLI, and watched the version settle FAILED. docs/lifecycle.md said this surface was needed for #18 and it was not; it now says what it is actually for.
The docs site mentioned -serve-sts in four places and the README in none. It is the flag that decides whether the KubeMicroVM operator starts at all, and the README is the page that leads with running the operator, so of everything that could have drifted this was the worst candidate. Found by diffing the runtime flags each surface names rather than by reading, which is the only way this stays true — #54 lists README-versus- docs drift as an open item precisely because they have drifted before.
This was referenced Aug 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #56. Ticks one of #54's four honest items.
m80 could force a build to
FAILEDand a connector to any of its seven reason codes, and only from Go. A test importing m80 could drive them; a suite pointed at the container could not reach them at all — backwards, since a container is what a consumer tests against.Three deliberate choices
Keyed by name, not the ARN the issue sketched. A lever arms before the resource exists — that is what "the next build of this image fails" means — so at the moment of arming there is no ARN to name it with.
The response carries
"injected": true. A state m80 reached under its own rules never carries it, so a consumer asserting on that field cannot mistake an injectedFAILEDfor a real one. That was the issue's first requirement, and it is why the endpoint returns a body rather than a bare 200.Off unless
-enable-injection. Nothing under/_m80/is signed, so anything reaching the port can arm a lever; the flag is the consent, same posture as-serve-sts. Without it the route is still registered and answers 404 naming the flag — the requirement was that the levers not be reachable by accident, not that they be undiscoverable, and a bare 404 is indistinguishable from a typo in the path.Verified against the container
Not just in-process. Built the image, armed a build over HTTP, created it with the AWS CLI, watched the version settle
FAILED:11 new tests, full suite and
-raceclean,scripts/smoke.shstill walks the README end to end.docs/lifecycle.mdclaimed this surface was needed for #18. It was not — that was corrected in the issue and the page now says what it is actually for.scope.md,api-surface.mdand the README all name the flag.