Português (Brasil) | Español (Argentina)
Experimental pre-release project. Molejo Testkit is a test fixture, not a production application.
Molejo Testkit is a small, deterministic container image for exercising HTTP applications, Kubernetes application delivery, and network policies. A single static Go binary provides REST, GraphQL, Server-Sent Events (SSE), WebSocket, health endpoints, and an explicit outbound probe command.
The public HTTP server and arbitrary network probe are intentionally separate. Server mode can contact only peers declared in an optional read-only file, so a public request cannot turn the workload into an outbound proxy. One-off network diagnostics remain an explicit container command.
| Capability | Interface |
|---|---|
| Language detection aliases | GET /, GET /websocket, GET /rest, GET /graphql-lab, GET /sse |
| Localized browser pages | GET /en/, GET /pt-BR/, GET /es-AR/ |
| Localized browser labs | GET /en/rest, /en/graphql-lab, /en/sse, /en/websocket and matching translated paths |
| Static asset | GET /static/style.css |
| Liveness | GET /healthz |
| Readiness | GET /readyz |
| Unready response | GET /not-ready |
| REST status | GET /api/status |
| REST collection | GET /api/items |
| REST echo | POST /api/echo |
| GraphQL | POST /graphql |
| Server-Sent Events | GET /events |
| WebSocket echo and broadcast | GET /ws |
| HTTP/HTTPS network probe | testkit probe URL |
| Configured peer identity and state | GET /api/identity, GET /api/peers |
The build version stored in the tracked VERSION file is embedded in the Go
binary and exposed in protocol payloads, structured logs, the browser header and
footer, and the Testkit-Version header on every HTTP response, including
successful WebSocket handshakes. This makes rollout and transport tests
observable without requiring external build arguments or changing the logical
identity of the workload.
- Go 1.26.x.
- Docker Engine or Docker Desktop with Buildx enabled.
curlfor the examples.
Build the image for the local Docker platform:
docker buildx build \
--load \
--tag molejo-testkit:dev \
.Run it using the restricted container contract expected by Molejo Platform:
docker run --rm \
--publish 8080:8080 \
--read-only \
--cap-drop ALL \
--security-opt no-new-privileges \
molejo-testkit:devVerify the REST contract:
curl --fail http://localhost:8080/api/statusExpected response:
{"status":"ok","version":"v0.7.1"}Open http://localhost:8080/ to let the server detect the browser language, or
use a localized URL directly: /en/, /pt-BR/ or /es-AR/. The browser labs
are available at the matching /rest, /graphql-lab, /sse and /websocket
paths. REST and GraphQL use guided presets that render request and response
details. The SSE lab displays event IDs, names and data, with explicit connect,
disconnect and reconnect actions. The WebSocket lab shows two independent
same-origin clients, Client A and Client B, so you can connect both panels,
send a message from either one, and switch between raw JSON events and a chat
view with local send/receive timestamps. Browser clients do not reconnect
automatically after an error or close.
REST echo:
curl --json '{"hello":"world"}' http://localhost:8080/api/echoGraphQL:
curl --json '{"query":"{ status version echo(message: \"hello\") }"}' \
http://localhost:8080/graphqlSSE:
curl -N http://localhost:8080/eventsWebSocket:
wscat --connect ws://localhost:8080/ws
> {"message":"hello"}Each connected WebSocket client receives the broadcast message with the current build version:
{"message":"hello","version":"v0.7.1"}Run network diagnostics as an explicit command of the same image:
docker run --rm molejo-testkit:dev probe https://example.com/The probe performs one bounded HTTP or HTTPS GET request and emits a JSON result.
Its exit codes form the automation contract:
| Exit code | Meaning |
|---|---|
0 |
The destination returned HTTP 2xx. |
1 |
The request failed or returned a non-2xx response. |
2 |
The command arguments or URL are invalid. |
To verify Kubernetes NetworkPolicies, run the probe as a short-lived Job in the namespace under test. Apply the labels and ServiceAccount whose network identity you want to validate, then assert the Job exit code. This keeps the source, destination, and expected allow-or-deny result explicit.
Server mode can continuously verify a fixed allowlist of other Testkit
instances. The feature is disabled unless TESTKIT_PEERS_FILE points to a
read-only JSON file:
{
"schema_version": 1,
"instance_id": "testkit-a",
"check_interval": "30s",
"timeout": "3s",
"peers": [
{
"name": "testkit-b",
"scheme": "http",
"host": "testkit-b.namespace-b",
"port": 8080,
"expected_instance_id": "testkit-b"
}
]
}Each check opens a fresh direct connection, ignores HTTP proxy environment
variables, does not follow redirects, and requests the fixed /api/identity
path. HTTPS uses the system trust store without an insecure mode. Responses are
limited to 4 KiB and at most four peers are checked concurrently. The first
check is immediate and later checks use check_interval; timeout must be
positive, no greater than 30 seconds, and shorter than that interval.
GET /api/identity returns the configured logical identity and a process-local
UUIDv7 boot_id. GET /api/peers returns only sanitized in-memory facts about
the latest checks. Both endpoints exist only when the peer file is configured.
An instance with no outbound peers can use "peers": [] to serve its identity.
Neither endpoint accepts a destination or starts an on-demand check, and both
responses use Cache-Control: no-store.
The outcomes are reachable, unreachable, and unknown. Reasons distinguish
DNS, connection, TLS, HTTP, response, and identity failures. These are observed
transport facts: Testkit never claims that a failure was caused by a
NetworkPolicy. A Service with multiple replicas proves reachability to the
Service, not to one specific Pod.
- Listens on TCP port
8080. - Runs as UID/GID
65532:65532. - Supports a read-only root filesystem.
- Requires no Linux capabilities or privilege escalation.
- Includes a CA bundle for HTTPS probes.
- Embeds all static assets in the binary.
- Builds reproducibly for BuildKit target platforms, including
linux/amd64andlinux/arm64. - Handles
SIGTERMand drains HTTP, SSE, and WebSocket connections with a bounded shutdown.
The HTTP server accepts same-origin WebSocket connections and clients that omit
the Origin header, such as command-line test clients. Cross-origin browser
connections are rejected.
| Variable | Default | Purpose |
|---|---|---|
SSE_INTERVAL |
1s |
Interval between SSE status events. |
TESTKIT_PEERS_FILE |
unset | Read-only peer configuration; unset disables peer monitoring and its HTTP endpoints. |
Invalid or non-positive SSE_INTERVAL values fall back to the default.
Server mode writes newline-delimited JSON logs to standard output. Every record
includes service and version. The primary event values are:
server.started,server.stopped, and server failure events;http.request.completedfor/api/status,/api/items, and/api/echo, with method, stable route, status code, duration in milliseconds, and response bytes;connection.openedandconnection.closedfor WebSocket and SSE, with the active protocol and total connection counts and connection duration on close;connections.snapshotevery 15 minutes while at least one connection is active.peer.identity.requestedfor configured peer identity requests;peer.state.changedwhen a configured peer outcome, reason, or remoteboot_idchanges;peers.snapshotevery 15 minutes while at least one peer is configured.
Connection lifecycle events and snapshots include connection_sequence, which
increases with every connection state transition in the process. Consumers can
use it to reconstruct transition order when concurrent log records arrive out of
order.
Completed REST requests and accepted WebSocket and SSE connections include a
UUIDv7 correlation_id. REST clients may provide it through
X-Testkit-Correlation-ID; WebSocket and SSE clients may use the
correlation_id query parameter. Missing or invalid values are replaced with a
server-generated ID. REST responses return the effective ID in the same header.
The bundled REST, WebSocket, and SSE labs generate and display these IDs
automatically. Connection snapshots remain aggregate and do not include
correlation IDs.
Connection counts represent accepted connections currently observed by one server process and reset on restart. Page refreshes, normal closes, and transport errors decrement the count when the server observes the disconnect. WebSocket heartbeats bound silent failure detection to about 60 seconds; SSE detection is best effort when a network path disappears without closing the HTTP stream. Graceful shutdown waits for accepted WebSocket handlers to emit their closing facts. A crash or forced process termination cannot emit closing facts, so consumers must treat each server start as a new process-local epoch.
Logs do not include client addresses, raw headers, raw query strings, request payloads, WebSocket messages, or SSE data. They are observable test facts, not durable or global metrics.
Peer facts use logical peer names and stable reasons. They do not log configured
hosts, resolved IP addresses, proxy settings, response bodies, or raw errors.
Peer state resets when the process restarts; boot_id identifies the local
epoch and observed_boot_id identifies the latest remote epoch. Checks canceled
by server shutdown do not replace the last observed peer state.
Tagged releases publish multi-platform images to GitHub Container Registry:
ghcr.io/molejo-platform/testkit:v0.7.1
Tags are provided for discovery. Automated tests should consume the immutable digest reported by the release workflow:
ghcr.io/molejo-platform/testkit@sha256:<digest>
The project does not publish a latest tag. Signing, SBOMs, and additional
provenance attestations are outside the current release contract.
Run the local quality gate:
gofmt -w *.go
go test -race -cover ./...
go vet ./...
go mod tidy -diff
node --test frontend-tests/ws-client.test.mjs frontend-tests/ws-ui.test.mjsBuild and exercise the final container whenever runtime, embedded assets, probes, or the Dockerfile changes.
English is the canonical documentation language. Available translations:
Translations preserve commands, paths, endpoint names, fields, and protocol identifiers in English. If translated content diverges, the English version defines the current contract.
See CONTRIBUTING.md before proposing a change.
Do not report vulnerabilities through public issues. Follow SECURITY.md.
Licensed under the Apache License 2.0.