Skip to content

feat(cli): panda buildoor — drive per-slot action plans and jq transforms#323

Open
qu0b wants to merge 4 commits into
masterfrom
qu0b/buildoor-action-plan-cli
Open

feat(cli): panda buildoor — drive per-slot action plans and jq transforms#323
qu0b wants to merge 4 commits into
masterfrom
qu0b/buildoor-action-plan-cli

Conversation

@qu0b

@qu0b qu0b commented Jul 23, 2026

Copy link
Copy Markdown
Member

Summary

Adds a panda buildoor command group so tests can hot-patch builder payloads on devnet buildoor instances from panda — the sanctioned replacement for baking overrides into buildoor itself (cf. ethpandaops/buildoor#146 reverting the gaslimit adjust in favor of action-plan jq transforms):

panda buildoor test-transform glamsterdam-devnet-7 lighthouse-geth-1 payload '.gas_limit = 300000000'
panda buildoor transform glamsterdam-devnet-7 lighthouse-geth-1 --slots +2,+3 --payload '.gas_limit = 300000000'

Commands

  • networks / instances <network> — discover devnets with buildoor and their builder instances (live from the overview service's host list; addressed by short name, e.g. prysm-ethrex-1)
  • overview — instance status (current slot, services)
  • plan / results — action plans and attempt-level outcome history (defaults to current−8..current+24 for plan)
  • transform — set/clear transforms.payload|bid|envelope jq expressions on future slots (--slots 123,+2 or --from/--to; +N/+-N resolve against the instance's current slot)
  • test-transform — run an expression through buildoor's production gojq path against a captured artifact or template, without touching any plan
  • plan-update — raw PlanUpdate JSON escape hatch for the non-transform categories (bid.bid_value_gwei, reveal gating, …)

Design

  • Server-side buildoor.* operations: resolve the network's buildoor overview URL from cartographoor, fetch /api/overview/hosts for per-instance API URLs, and proxy the instance action-plan API. Buildoor keeps ownership of the PlanUpdate schema — updates pass through verbatim, and its errors surface with remedies (bad jq → 400, frozen/past slot → 409 + "target slots ≥2 ahead", missing token → 401 + token hint).

  • Auth — the proxy is the credential boundary (matching the platform's authentik/workflow/ethnode patterns): the proxy gains a /buildoor/{network}/{instance}/api/* route that mints per-devnet authenticatoor JWTs with a proxy-held CF Access service token (auth.<network>/auth/token, allowServiceTokens — subject = the token's common name), caches them, and injects them upstream; allowed_orgs gates the route and hides the discovery advert, and the server routes mutations to the first proxy advertising buildoor. An explicit --token / PANDA_BUILDOOR_TOKEN still goes direct and keeps per-user attribution in buildoor's own audit log (proxy path: human attributed in the proxy audit, service subject in buildoor's).

    Hosted activation checklist (platform): create a CF Access service token, add it to the devnet auth applications' Access policy, and set buildoor: {cf_access_client_id/secret, allowed_orgs: [ethpandaops:Core]} in panda-proxy values. static_token supports local proxies.

  • Python surface: a buildoor module (DefaultEnabled, mirrors dora) exposes the same operations to sandbox code — from ethpandaops import buildoor; buildoor.set_transforms(net, inst, token, slots=[s+2], payload='.gas_limit = 300000000') — with Python API docs and searchable examples. Needs a sandbox image rebuild (modules/*/python/*.py is copied in at build time).

  • Includes a runbooks://buildoor_action_plan runbook covering the workflow, freeze semantics, and failure modes (CLI + Python surfaces).

Testing

  • go test ./pkg/server ./pkg/cli ./runbooks (incl. race), go vet, golangci-lint: clean.
  • Live against glamsterdam-devnet-7: instance discovery found all 4 builders; test-transform returned the transformed template payload with gas_limit: 300000000; plan/results reads with relative slot offsets resolved correctly; unsynced instance (current_slot=0) fails fast; bad token → 401 with hint.
  • Not verified live: a successful authenticated mutation (needs a real SSO-minted token); token/updates forwarding is covered by server tests.

qu0b added 4 commits July 23, 2026 15:24
…orms

Adds a buildoor command group so tests can hot-patch builder payloads from
panda, the sanctioned replacement for baking overrides into buildoor itself
(cf. buildoor#146 reverting the gaslimit adjust):

  panda buildoor transform <network> <instance> --slots +2,+3 \
    --payload '.gas_limit = 300000000'

Server-side buildoor.* operations resolve a network's instances through the
buildoor overview service advertised by cartographoor, proxy the instance
action-plan API (plans, slot results, overview, test-transform), and pass
mutations through verbatim with the caller's authenticatoor bearer token —
buildoor keeps ownership of the PlanUpdate schema, validation, and auditing.

The CLI resolves +N/+-N slot offsets against the instance's current slot,
exposes test-transform for validating jq expressions against captured
artifacts before committing them, and offers plan-update as the raw
PlanUpdate escape hatch for non-transform categories.
Companion runbook for the panda buildoor command group: instance discovery,
transform validation via test-transform, freeze semantics, authenticatoor
tokens, and outcome verification through slot results.
…python

Registers a buildoor module (DefaultEnabled, mirrors dora) so sandbox code
can drive the same buildoor.* operations as the CLI:

  from ethpandaops import buildoor
  buildoor.set_transforms(net, inst, token, slots=[s+2],
                          payload='.gas_limit = 300000000')

Ships the python wrapper (list_networks/list_instances/get_overview/
get_action_plan/get_slot_results/test_transform/update_action_plan/
set_transforms), sandbox env gating via ETHPANDAOPS_BUILDOOR_NETWORKS,
Python API docs, and searchable examples. Requires a sandbox image rebuild
(modules/*/python/*.py is copied in at build time).
…redential boundary

Corrects the auth design: mutations no longer require users to hand-carry
devnet tokens. Following the platform's established patterns (authentik OIDC
at the proxy, workflow-style capability advert, ethnode-style dynamic devnet
routing), the proxy now owns the devnet credential:

- proxy: /buildoor/{network}/{instance}/api/* route. The proxy mints a JWT
  per devnet from its authenticatoor (auth.<network>/auth/token) using a
  CF Access service token (authenticatoor allowServiceTokens; subject =
  token common name), caches it until near expiry, and injects it as the
  upstream bearer. static_token supports local setups. allowed_orgs gates
  the route and hides the advert (mirrors workflow); only /api/ paths proxy.
- discovery: BuildoorAdvert capability + BuildoorInfoProvider/BuildoorRoute
  (client + router), so the server routes mutations to the first proxy that
  advertises buildoor.
- server: update_action_plan prefers the proxy route; an explicit caller
  token still goes direct and keeps per-user attribution in buildoor's own
  audit log (the proxy path attributes the human via the proxy audit).
- CLI/Python: token now optional; help and runbook describe both paths.

Verified end-to-end against glamsterdam-devnet-7: CLI (no token) → server →
proxy route → live instance; the injected bearer reached buildoor and its
401 for the test token propagated back with the remedy hint.

Hosted activation needs platform provisioning: a CF Access service token in
the devnet auth applications' Access policy + the buildoor block (secret) in
panda-proxy values.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant