Skip to content

Commit 1584c50

Browse files
feat(scripts): import member signing keys from Nomad job definitions
migrate-dev-nodes.sh now takes the network as its first argument and, before the image swap, pulls MPC_ACCOUNT_ID/MPC_ACCOUNT_SK from each job's Env and imports any key missing from the local near-cli keystore. The secret is masked in the echoed command. test_sign keeps a manual interactive import (ensure_signing_key) as fallback. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 6c4c161 commit 1584c50

5 files changed

Lines changed: 85 additions & 7 deletions

File tree

RELEASES.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,11 @@ confirmation prompt, so a run can be stopped at any step. Nothing
180180
cluster-specific is stored in this repo; addresses and credentials are typed in
181181
per run, or supplied through the per-network `NOMAD_ADDR_DEV_*`,
182182
`NOMAD_HTTP_AUTH_DEV_*`, and `MPC_NODE_ADDRS_DEV_*` environment variables to
183-
skip the matching prompt.
183+
skip the matching prompt. The on-chain steps sign with near-cli; the node step
184+
imports member-account keys it finds in the Nomad job definitions into the
185+
local keystore, and the test-signature step offers a manual
186+
`near account import-account` as fallback. Keys are masked in the echoed
187+
commands.
184188

185189
## Re-running after a failure
186190

scripts/ops/dev-cluster/dev-common.sh

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,28 @@ nomad_auth_state() {
8080
else echo "(none)"; fi
8181
}
8282

83+
# Probes the legacy ~/.near-credentials layout only; OS-keychain keys can't be.
84+
have_signing_key() {
85+
[[ -f "${HOME}/.near-credentials/${NEAR_NET}/${1}.json" ]]
86+
}
87+
88+
# Best-effort: a miss is advisory (the key may be in the OS keychain). Offers
89+
# near-cli's interactive import; the key is typed into near-cli's own prompt.
90+
ensure_signing_key() {
91+
local account=$1
92+
have_signing_key "$account" && return 0
93+
warn "No ~/.near-credentials/${NEAR_NET}/${account}.json — fine if the key is in the OS keychain."
94+
confirm "Import a key for ${account} with near-cli now?" || return 0
95+
run_cmd near account import-account \
96+
|| warn "Import did not complete — signing as ${account} may fail."
97+
}
98+
99+
# Read-only contract query against the resolved cluster.
100+
near_view() {
101+
run_cmd near contract call-function as-read-only "$CONTRACT" "$1" \
102+
json-args '{}' network-config "$NEAR_NET" now
103+
}
104+
83105
# Retries per node (warm-up delay after allocation starts).
84106
verify_nodes() {
85107
local version=$1
@@ -115,6 +137,7 @@ test_sign() {
115137
require_cmds near
116138

117139
local signer=${MEMBER_ACCOUNTS%% *}
140+
ensure_signing_key "$signer"
118141
local payload='[12,1,2,0,4,5,6,8,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,44]'
119142
local cmd=(near contract call-function as-transaction "$CONTRACT" sign
120143
json-args "{\"request\": {\"payload\": ${payload}, \"path\": \"test\", \"key_version\": 0}}"

scripts/ops/dev-cluster/dev-menu.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ resolve_version() {
4444

4545
resolve_network "${1:-}"
4646
resolve_version "${2:-}"
47-
4847
resolve_dev_cluster "$NETWORK"
48+
4949
prompt_nomad_ip "$NETWORK"
5050
[[ -n "${NOMAD_HTTP_AUTH+set}" ]] || prompt_http_auth
5151
prompt_node_addrs
@@ -61,7 +61,7 @@ EOF
6161
confirm "Proceed?" || { echo "Aborted."; exit 0; }
6262

6363
step "### Step 1 — nodes"
64-
run_cmd "${SCRIPT_DIR}/migrate-dev-nodes.sh" "$VERSION" \
64+
run_cmd "${SCRIPT_DIR}/migrate-dev-nodes.sh" "$NETWORK" "$VERSION" \
6565
|| die "Node upgrade did not complete."
6666

6767
step "### Verify"
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#!/usr/bin/env bash
2+
#
3+
# first-time-setup.sh — first-run helpers that copy the member accounts'
4+
# signing keys from the Nomad job definitions into the local near-cli
5+
# keystore (source, don't run). The node signs with MPC_ACCOUNT_SK already,
6+
# so the key is on-chain — only the operator's local copy can be missing.
7+
#
8+
9+
# "account sk" per MPC task, from a job definition on stdin. Secrets injected
10+
# via a template stanza instead of Env come out empty and are skipped upstream.
11+
job_signing_creds() {
12+
jq -r --arg prefix "$NODE_IMAGE_PREFIX" \
13+
'.TaskGroups[].Tasks[]
14+
| select(.Config.image // "" | startswith($prefix))
15+
| "\(.Env.MPC_ACCOUNT_ID // "") \(.Env.MPC_ACCOUNT_SK // "")"'
16+
}
17+
18+
# near-cli offers no stdin path for the key, so it rides argv (visible in
19+
# /proc for its lifetime) — but it is masked in the echoed command.
20+
import_signing_key() {
21+
local account=$1 sk=$2
22+
show_cmd near account import-account using-private-key '<MPC_ACCOUNT_SK>' \
23+
network-config "$NEAR_NET"
24+
near account import-account using-private-key "$sk" network-config "$NEAR_NET" \
25+
|| warn "Import did not complete — signing as ${account} may fail."
26+
}
27+
28+
# Imports any missing member-account keys found in a job's definition.
29+
ensure_job_keys() {
30+
local job_id=$1 job account sk
31+
job=$(nomad_curl GET "/job/${job_id}") || { warn "Could not fetch ${job_id}."; return; }
32+
while read -r account sk; do
33+
[[ -n "$account" && -n "$sk" ]] || continue
34+
if have_signing_key "$account"; then
35+
ok "${account}: key already in ~/.near-credentials."
36+
else
37+
import_signing_key "$account" "$sk"
38+
fi
39+
done < <(job_signing_creds <<<"$job")
40+
}

scripts/ops/dev-cluster/migrate-dev-nodes.sh

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44
# mpc-node-* Nomad job to nearone/mpc-node-gcp:<VERSION> — plan, confirm, run.
55
# Verification is the caller's job (dev-menu.sh runs it next).
66
#
7-
# Usage: ./scripts/ops/dev-cluster/migrate-dev-nodes.sh <VERSION>
7+
# Usage: ./scripts/ops/dev-cluster/migrate-dev-nodes.sh <testnet|mainnet> <VERSION>
88
# The Nomad IP address and its basic-auth credentials are prompted for. Exporting
99
# NOMAD_ADDR / NOMAD_HTTP_AUTH="user:password" skips the matching prompt;
1010
# NOMAD_TOKEN adds an ACL token header; NOMAD_NAMESPACE targets that namespace.
11+
# Member-account keys found in the job definitions are imported into the local
12+
# near-cli keystore if missing, so the later signing steps can run.
1113
#
1214

1315
set -euo pipefail
@@ -17,6 +19,8 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
1719
source "${SCRIPT_DIR}/../common.sh"
1820
# shellcheck source=dev-common.sh
1921
source "${SCRIPT_DIR}/dev-common.sh"
22+
# shellcheck source=first-time-setup.sh
23+
source "${SCRIPT_DIR}/first-time-setup.sh"
2024

2125
# curl -K parses values as quoted strings with backslash escapes.
2226
curl_cfg_escape() {
@@ -137,10 +141,12 @@ upgrade_nomad_job() {
137141
wait_for_alloc "$job_id"
138142
}
139143

140-
[[ $# -eq 1 ]] || die "Usage: $0 <VERSION> (e.g. 3.14.0)"
141-
VERSION=$1
144+
[[ $# -eq 2 ]] || die "Usage: $0 <testnet|mainnet> <VERSION> (e.g. testnet 3.14.0)"
145+
NETWORK=$1
146+
VERSION=$2
142147
check_version "$VERSION"
143-
require_cmds curl jq
148+
require_cmds curl jq near
149+
resolve_dev_cluster "$NETWORK"
144150
prompt_nomad_ip
145151
[[ -n "${NOMAD_HTTP_AUTH+set}" ]] || prompt_http_auth
146152

@@ -158,6 +164,11 @@ JOB_IDS=$(nomad_curl GET "/jobs?prefix=mpc-node" | jq -r '.[].ID') \
158164
|| die "Could not list jobs from ${NOMAD_ADDR}."
159165
[[ -n "$JOB_IDS" ]] || die "No mpc-node-* jobs found at ${NOMAD_ADDR}."
160166

167+
step "==> Local signing keys"
168+
for job_id in $JOB_IDS; do
169+
ensure_job_keys "$job_id"
170+
done
171+
161172
for job_id in $JOB_IDS; do
162173
upgrade_nomad_job "$job_id" "$IMAGE"
163174
done

0 commit comments

Comments
 (0)