Skip to content

feat(sidecar): sign-tx task family (gov vote / submit-proposal / deposit) #163

@bdchatham

Description

@bdchatham

Problem

The sidecar's task engine today exposes lifecycle, query, and one-shot mutation tasks but no chain-transaction-signing tasks. To enable in-pod governance operations (the migration target for slanders/seienv once validators move to Kubernetes), the sidecar needs a task family that signs and broadcasts Cosmos SDK messages using the mounted operator-account keyring.

seienv today SSHes into validator hosts and runs printf "12345678\n" | sudo /root/go/bin/seid tx gov vote ... — works for EC2 + systemd, doesn't translate to containers. The sidecar should sign in-process using Cosmos SDK libraries, not shell out to seid. Precedent: sidecar/tasks/generate_gentx.go already imports sei-cosmos/client/tx and sei-cosmos/crypto/keyring and signs gentx transactions via library calls.

Impact

  • Final on-cluster execution piece for K8s validator governance flows.
  • Establishes the pattern for future tx-signing tasks (staking, distribution, IBC).
  • Replaces the SSH + seid tx shell-out pattern from slanders/seienv.

⚠️ Security posture during the gap before authn lands

This task family is being shipped before sidecar API authentication (sister issue: "Sidecar authn + authz middleware on mutating endpoints"), which is the explicit last item in this sequence per a deliberate team scheduling decision.

The current deployment posture treats the sidecar API as accessible only to actors with kubectl-exec / port-forward / cluster-network access — parallel to today's SSH-key boundary on EC2 hosts in the seienv flow (anyone with the SSH key has equivalent power; here, anyone with cluster network access does).

Operators MUST NOT expose the sidecar API to multi-tenant clusters or untrusted in-cluster workloads until the authn issue lands. Acceptance criterion below includes a warning comment at the top of each new task handler referencing the authn issue.

Relevant experts

  • blockchain-developer — Msg semantics, fee/gas calculation, account sequence handling
  • platform-engineer — task engine integration, idempotency, error handling
  • security-specialist — review of pre-authn warning comments + chain-confusion guard

Proposed approach

Library-based signing (NOT shell-out to seid binary) using the SDK packages already in go.mod. Pattern follows sidecar/tasks/generate_gentx.go.

  1. New task types in sidecar/engine/types.go:
    • TaskTypeGovVote — params: proposalId, option (yes|no|abstain|no_with_veto), keyName, memo (opt), gasFees (opt)
    • TaskTypeGovSubmitProposal — params: proposalType (initially software-upgrade), proposal-specific subparams, keyName, deposit, memo, gasFees
    • TaskTypeGovDeposit — params: proposalId, amount, keyName, gasFees
  2. Handlers in sidecar/tasks/gov_vote.go, gov_submit_proposal.go, gov_deposit.go. Shared helper sidecar/tasks/sign_and_broadcast.go:
    • Builds client.Context from sidecar env + keyring factory (sister issue: "production keyring backend")
    • Constructs tx.Factory, marshals the typed Msg, signs, broadcasts via the existing sidecar/rpc/client.go
    • Returns {txHash, height, rawLog} in the task result
  3. Account sequence/number resolution: query the node's auth module via the RPC client at sign time. Cache per-key for the duration of a single task; do NOT persist across tasks (avoids stale-sequence retry hazards).
  4. Chain-confusion guard: every handler refuses if params.chainId != os.Getenv("SEI_CHAIN_ID") — defense against accidental cross-chain key reuse.
  5. Idempotency: relies on existing engine UUID dedupe (sidecar/engine/engine.go:91-138). Critical: a retry with the same caller-supplied UUID after a successful broadcast must NOT re-broadcast. Documented loudly in the task type docstrings.
  6. Per-Msg typed param schemas (not a generic signAndBroadcast with raw Msg bytes) — better validation, better audit, easier authn-policy enforcement when authn lands.
  7. OpenAPI schema update: sidecar/api/openapi.yaml adds new task type enum values + per-type param schemas.
  8. Client SDK helpers in sidecar/client/tasks.go: SubmitGovVoteTask(...), SubmitGovSubmitProposalTask(...), SubmitGovDepositTask(...).

Acceptance criteria

  • Three task types implemented with typed params
  • Shared sign_and_broadcast.go helper used by all three handlers
  • OpenAPI schema updated
  • Client SDK helpers added in sidecar/client/tasks.go
  • Chain-confusion guard test (vote with wrong chain-id rejected)
  • Idempotent-retry test (same UUID twice → same tx hash, no double-broadcast)
  • Stale-sequence retry test (sequence refreshed on retry, not cached across tasks)
  • Integration test against a local seid devnet: vote, submit-proposal, deposit
  • Pre-authn warning comment present at top of each new handler file referencing the authn issue
  • README and OpenAPI carry "unauthenticated — see authn issue" notice until authn ships

Out of scope

  • Authn middleware (separate issue, scheduled last)
  • Staking (MsgDelegate / MsgUndelegate), distribution (MsgWithdrawDelegatorReward), IBC — same pattern, separate issues if needed
  • Multi-msg transactions (single Msg per task for now)
  • EVM transactions

References

  • Coral session 2026-05-11
  • Reference impl: sidecar/tasks/generate_gentx.go (in-process SDK signing already in this sidecar)
  • Reference impl: sei-cosmos x/gov/client/cli/tx.go (the seid CLI's own impl — direct copy-shape target)
  • Existing RPC client: sidecar/rpc/client.go
  • Idempotency machinery: sidecar/engine/engine.go:91-138
  • Predecessor: slanders/seienv cmd/vote.go, propose.sh
  • Sister issues: operator-keyring CRD surface (sei-protocol/sei-node-controller), production keyring backend (this repo), seictl gov CLI (this repo), sidecar authn (this repo)

Sequencing

Phase 1, step 3 of 5 in the governance-flow migration. Depends on steps 1 (CRD operator-keyring in sei-node-controller) and 2 (sidecar keyring backend). Blocks step 4 (seictl CLI commands). Step 5 (authn) is the final hardening pass and lands after this.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions