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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions