@consiliency/agent-board-cli 1.24.0 → 1.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/claude-marketplace/.claude-plugin/marketplace.json +14 -0
  2. package/claude-marketplace/plugins/agent-board/.claude-plugin/plugin.json +19 -0
  3. package/claude-marketplace/plugins/agent-board/hooks/hooks.json +25 -0
  4. package/claude-marketplace/plugins/agent-board/hooks/wake-hook.sh +288 -0
  5. package/dist/assets.d.ts +26 -0
  6. package/dist/assets.js +70 -0
  7. package/dist/assets.json +6 -0
  8. package/dist/claude_marketplace.d.ts +117 -0
  9. package/dist/claude_marketplace.js +306 -0
  10. package/dist/cli.d.ts +37 -3
  11. package/dist/cli.js +68 -26
  12. package/dist/config_edit.d.ts +37 -0
  13. package/dist/config_edit.js +549 -0
  14. package/dist/doctor.d.ts +24 -2
  15. package/dist/doctor.js +164 -4
  16. package/dist/doctor_scopes.d.ts +34 -2
  17. package/dist/doctor_scopes.js +128 -20
  18. package/dist/identity_writer.d.ts +9 -7
  19. package/dist/identity_writer.js +18 -30
  20. package/dist/setup.d.ts +116 -0
  21. package/dist/setup.js +603 -0
  22. package/dist/setup_storage.d.ts +117 -0
  23. package/dist/setup_storage.js +447 -0
  24. package/dist/setup_writers.d.ts +147 -0
  25. package/dist/setup_writers.js +916 -0
  26. package/keyslot/agent-board-keyslot-load@.service +20 -0
  27. package/keyslot/agent-board-keyslot@.service +20 -0
  28. package/keyslot/constants.sh +73 -0
  29. package/keyslot/keygen.mjs +67 -0
  30. package/keyslot/keyslot-load.sh +78 -0
  31. package/keyslot/lib.sh +599 -0
  32. package/keyslot/optin.sh +185 -0
  33. package/keyslot/probe.sh +135 -0
  34. package/keyslot/provision.sh +530 -0
  35. package/keyslot/seam.sh +48 -0
  36. package/keyslot/status.sh +254 -0
  37. package/package.json +9 -4
@@ -0,0 +1,185 @@
1
+ # shellcheck shell=bash
2
+ # The unsealed slot's validation (A1), ONE predicate shared by keyslot-load.sh, which runs it
3
+ # before every load (boot included), and by status.sh and provision.sh (through lib.sh). The
4
+ # loader therefore never loads a key that status would call a contradiction. In the install
5
+ # set, so it runs with the checkout gone. Needs constants.sh and seam.sh sourced first.
6
+ #
7
+ # Principle 4 in bash: every ancestor directory is a real directory (never a symlink), owned by
8
+ # the user or root, and not group/other-writable unless root-owned and sticky (/tmp). The file
9
+ # itself is refused unless its lstat is a regular file (so a FIFO is never opened, and never
10
+ # blocks a boot), then OPENED, and judged on the opened descriptor (/proc/self/fd/N): the same
11
+ # inode as the lstat, a regular file, the user's, no group/other bit. Every consumer reads that
12
+ # descriptor: the opt-in's lines, ssh-keygen's derivation (/proc/self/fd/N), and the loader's
13
+ # ssh-add, which is fed the descriptor it checked. File types come from stat's numeric mode
14
+ # (%f), never its translated names.
15
+ #
16
+ # Every function prints its problems, one per line, and returns 0 only when there are none.
17
+
18
+ # keyseal_trusted_ancestors <dir>: <dir> and every directory above it.
19
+ keyseal_trusted_ancestors() {
20
+ local dir=$1 path=/ part uid rc=0
21
+ local -a parts
22
+ uid=$("$KEYSEAL_BIN_ID" -u)
23
+ [[ $dir == /* ]] || { printf '%s is not absolute\n' "$dir"; return 1; }
24
+ IFS=/ read -ra parts <<<"${dir#/}"
25
+ keyseal_trusted_dir_entry / "$uid" || rc=1
26
+ for part in "${parts[@]}"; do
27
+ [[ -n $part ]] || continue
28
+ path=${path%/}/$part
29
+ keyseal_trusted_dir_entry "$path" "$uid" || { rc=1; [[ -e $path ]] || return 1; }
30
+ done
31
+ return "$rc"
32
+ }
33
+
34
+ # One directory, not followed: a real directory, the user's or root's, not writable by others
35
+ # unless root-owned and sticky.
36
+ # The file type of a raw stat mode (%f, hex): dir, link, reg, or other.
37
+ keyseal_ftype() {
38
+ case $(((16#$1) & 16#F000)) in
39
+ $((16#4000))) printf dir ;;
40
+ $((16#A000))) printf link ;;
41
+ $((16#8000))) printf reg ;;
42
+ *) printf other ;;
43
+ esac
44
+ }
45
+
46
+ keyseal_trusted_dir_entry() {
47
+ local path=$1 uid=$2 info type owner mode
48
+ info=$("$KEYSEAL_BIN_STAT" -c '%f|%u|%a' -- "$path" 2>/dev/null) || { printf 'ancestor %s: missing\n' "$path"; return 1; }
49
+ type=$(keyseal_ftype "${info%%|*}") info=${info#*|}
50
+ owner=${info%%|*} mode=$((8#${info#*|}))
51
+ if [[ $type == link ]]; then printf 'ancestor %s is a symlink\n' "$path"; return 1; fi
52
+ if [[ $type != dir ]]; then printf 'ancestor %s is not a directory\n' "$path"; return 1; fi
53
+ local rc=0
54
+ if [[ $owner != "$uid" && $owner != 0 ]]; then printf 'ancestor %s is owned by uid %s\n' "$path" "$owner"; rc=1; fi
55
+ if ((mode & 8#022)) && ! { ((mode & 8#1000)) && [[ $owner == 0 ]]; }; then
56
+ printf 'ancestor %s is group/other-writable\n' "$path"; rc=1
57
+ fi
58
+ return "$rc"
59
+ }
60
+
61
+ # keyseal_trusted_open <path> <max-bytes>: principle 4 on the file. On success, KEYSEAL_FD is
62
+ # an open read descriptor on it (the caller closes it).
63
+ keyseal_trusted_open() {
64
+ local p=$1 max=$2 name=${1##*/} before after info uid rc=0
65
+ uid=$("$KEYSEAL_BIN_ID" -u)
66
+ KEYSEAL_FD=
67
+ keyseal_trusted_ancestors "${p%/*}" || rc=1
68
+ if [[ -L $p ]]; then printf '%s is a symlink\n' "$name"; return 1; fi
69
+ before=$("$KEYSEAL_BIN_STAT" -c '%f|%d:%i' -- "$p" 2>/dev/null) || { printf '%s: missing\n' "$name"; return 1; }
70
+ # Judged BEFORE the open: opening a FIFO blocks until a writer appears.
71
+ [[ $(keyseal_ftype "${before%%|*}") == reg ]] || { printf '%s is not a regular file\n' "$name"; return 1; }
72
+ before=${before#*|}
73
+ exec {KEYSEAL_FD}<"$p" || { printf '%s: unreadable\n' "$name"; KEYSEAL_FD=; return 1; }
74
+ info=$("$KEYSEAL_BIN_STAT" -L -c '%d:%i|%f|%u|%a|%s' -- "/proc/self/fd/$KEYSEAL_FD" 2>/dev/null) || info=
75
+ after=${info%%|*}
76
+ local rest=${info#*|}
77
+ local type=${rest%%|*}; rest=${rest#*|}
78
+ local owner=${rest%%|*}; rest=${rest#*|}
79
+ local mode=${rest%%|*} size=${rest#*|}
80
+ if [[ -z $info || $after != "$before" ]]; then printf '%s changed while it was opened\n' "$name"; rc=1
81
+ else
82
+ [[ $(keyseal_ftype "$type") == reg ]] || { printf '%s is not a regular file\n' "$name"; rc=1; }
83
+ [[ $owner == "$uid" ]] || { printf '%s is owned by uid %s\n' "$name" "$owner"; rc=1; }
84
+ (((8#$mode & 8#077) == 0)) || { printf '%s has mode %s, not 0600\n' "$name" "$mode"; rc=1; }
85
+ ((size <= max)) || { printf '%s is over %s bytes\n' "$name" "$max"; rc=1; }
86
+ fi
87
+ if ((rc)); then exec {KEYSEAL_FD}<&-; KEYSEAL_FD=; fi
88
+ return "$rc"
89
+ }
90
+
91
+ # keyseal_machine_id_of <file>: the opt-in's host comparand, sha256(file)[:16].
92
+ keyseal_machine_id_of() {
93
+ local h
94
+ h=$("$KEYSEAL_BIN_SHA256SUM" <"$1" 2>/dev/null) || return 1
95
+ printf '%s' "${h:0:16}"
96
+ }
97
+
98
+ # keyseal_optin_verify <optin> <pub> <slot> <uid> <machine_id>: the opt-in record, opened under
99
+ # principle 4, holds exactly the frozen fields, and each matches (IF-0-KEYSEAL-6).
100
+ keyseal_optin_verify() {
101
+ local path=$1 pub=$2 slot=$3 uid=$4 mid=$5 line k v rc=0 h
102
+ local -A rec=()
103
+ local fields=(version machine_id uid slot pub_sha256 granted_at)
104
+ keyseal_trusted_open "$path" 4096 || return 1
105
+ while IFS= read -r line <&"$KEYSEAL_FD" || [[ -n $line ]]; do
106
+ [[ -z $line ]] && continue
107
+ k=${line%%=*} v=${line#*=}
108
+ if [[ $line != *=* || " ${fields[*]} " != *" $k "* ]]; then printf 'unknown or malformed line "%s"\n' "$k"; rc=1; continue; fi
109
+ if [[ -n ${rec[$k]+x} ]]; then printf 'duplicate %s\n' "$k"; rc=1; continue; fi
110
+ rec[$k]=$v
111
+ done
112
+ exec {KEYSEAL_FD}<&-
113
+ local -A grammar=([version]='^1$' [machine_id]='^[0-9a-f]{16}$' [uid]='^[0-9]+$' [slot]="$KEYSEAL_SLOT_RE"
114
+ [pub_sha256]='^[0-9a-f]{64}$' [granted_at]='^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$')
115
+ for k in "${fields[@]}"; do
116
+ if [[ -z ${rec[$k]+x} ]]; then printf 'missing %s\n' "$k"; rc=1
117
+ elif [[ ! ${rec[$k]} =~ ${grammar[$k]} ]]; then printf 'malformed %s\n' "$k"; rc=1
118
+ fi
119
+ done
120
+ h=
121
+ if [[ -f $pub && ! -L $pub ]]; then h=$("$KEYSEAL_BIN_SHA256SUM" <"$pub" 2>/dev/null) && h=${h%% *} || h=; fi
122
+ [[ -z ${rec[machine_id]+x} || ${rec[machine_id]} == "$mid" ]] || { printf "machine_id is another machine's\n"; rc=1; }
123
+ [[ -z ${rec[uid]+x} || ${rec[uid]} == "$uid" ]] || { printf "uid is another user's\n"; rc=1; }
124
+ [[ -z ${rec[slot]+x} || ${rec[slot]} == "$slot" ]] || { printf "slot is another slot's\n"; rc=1; }
125
+ [[ -z ${rec[pub_sha256]+x} || ${rec[pub_sha256]} == "$h" ]] || { printf 'pub_sha256 does not match the pin file\n'; rc=1; }
126
+ return "$rc"
127
+ }
128
+
129
+ # keyseal_keyfile_pub <key>: the key file under principle 4, then the raw Ed25519 public key
130
+ # (43-char base64url) ssh-keygen derives from it, read through the opened descriptor.
131
+ keyseal_keyfile_pub() {
132
+ local key=$1 rc=0
133
+ keyseal_trusted_open "$key" 65536 || return 1
134
+ keyseal_fd_pub "$KEYSEAL_FD" "${key##*/}" || rc=1
135
+ exec {KEYSEAL_FD}<&-
136
+ return "$rc"
137
+ }
138
+
139
+ # keyseal_fd_pub <fd> <name>: the raw Ed25519 public key ssh-keygen derives from the open
140
+ # descriptor <fd>. ssh-keygen is resolved through the seam, as probe.sh resolves it (N5).
141
+ keyseal_fd_pub() {
142
+ local fd=$1 name=$2 out wire raw keygen
143
+ keygen=$(seam_resolve KEYSEAL_TEST_SSH_KEYGEN "$KEYSEAL_BIN_SSH_KEYGEN") || { printf '%s: ssh-keygen refused by the seam\n' "$name"; return 1; }
144
+ out=$("$keygen" -y -f "/proc/self/fd/$fd" 2>/dev/null) || out=
145
+ [[ $out == "ssh-ed25519 "* ]] || { printf '%s is not a usable Ed25519 key\n' "$name"; return 1; }
146
+ wire=${out#ssh-ed25519 }
147
+ wire=${wire%% *}
148
+ # The wire blob is string("ssh-ed25519") + string(32 raw bytes): 51 bytes, 68 base64
149
+ # characters, with this fixed prefix; the raw key is its last 32 bytes.
150
+ [[ ${#wire} == 68 && $wire == AAAAC3NzaC1lZDI1NTE5AAAAI* ]] || { printf '%s is not a usable Ed25519 key\n' "$name"; return 1; }
151
+ raw=$(printf '%s' "$wire" | "$KEYSEAL_BIN_BASE64" -d 2>/dev/null | "$KEYSEAL_BIN_TAIL" -c 32 | "$KEYSEAL_BIN_BASE64" -w0) || raw=
152
+ raw=${raw//+/-}
153
+ raw=${raw//\//_}
154
+ raw=${raw%%=*}
155
+ [[ $raw =~ ^[A-Za-z0-9_-]{43}$ ]] || { printf '%s is not a usable Ed25519 key\n' "$name"; return 1; }
156
+ printf '%s' "$raw"
157
+ }
158
+
159
+ # keyseal_unsealed_verify <slot> <uid> <machine_id>: the whole unsealed predicate. A valid
160
+ # opt-in, a 0600 key of this user, and the key's public key equal to the pin in <slot>.pub.
161
+ # On success prints the pin it verified (the loader compares its own descriptor with THAT value,
162
+ # never a re-read of <slot>.pub); otherwise prints the problems. rc 0 only when the slot may be
163
+ # loaded. The pin file is read under principle 4 too, so a FIFO there never blocks.
164
+ keyseal_unsealed_verify() {
165
+ local slot=$1 uid=$2 mid=$3 K pin= kp line rc=0
166
+ K=$(keyseal_key_dir)
167
+ # Called directly (not in $(...)): the descriptor must be open in THIS shell to be read.
168
+ if keyseal_trusted_open "$K/$slot.pub" 256; then
169
+ IFS= read -r pin <&"$KEYSEAL_FD" || true
170
+ exec {KEYSEAL_FD}<&-
171
+ fi
172
+ [[ $pin =~ ^[A-Za-z0-9_-]{43}$ ]] || { printf 'pin missing or malformed\n'; rc=1; }
173
+ if [[ -e $K/$slot.unsealed-optin || -L $K/$slot.unsealed-optin ]]; then
174
+ keyseal_optin_verify "$K/$slot.unsealed-optin" "$K/$slot.pub" "$slot" "$uid" "$mid" || rc=1
175
+ else
176
+ printf 'no opt-in record\n'; rc=1
177
+ fi
178
+ if kp=$(keyseal_keyfile_pub "$K/$slot.key"); then
179
+ [[ -z $pin || $kp == "$pin" ]] || { printf 'the pin does not derive from %s.key\n' "$slot"; rc=1; }
180
+ else
181
+ printf '%s\n' "$kp"; rc=1
182
+ fi
183
+ ((rc)) || printf '%s' "$pin"
184
+ return "$rc"
185
+ }
@@ -0,0 +1,135 @@
1
+ #!/bin/bash
2
+ # probe.sh [--json]: can this host hold a key slot, and in which mode? (IF-0-KEYSEAL-4,
3
+ # IF-0-KEYSEAL-6)
4
+ #
5
+ # Seals a random canary under a throwaway credential name, decrypts it IN A PIPE and
6
+ # compares the bytes. Also reports linger, the user manager's state, the systemd version and
7
+ # the absolute binaries the loader and the operator tools use, then removes its artefacts.
8
+ #
9
+ # Four outcomes, frozen (A1), in this precedence:
10
+ # error anything the probe cannot judge: no XDG_RUNTIME_DIR, a user manager
11
+ # that is not running/degraded, linger unreadable, an operator binary
12
+ # missing, an unreadable systemd version, and ANY failed round trip
13
+ # on systemd >= 256. Never `incapable`: provision refuses.
14
+ # incapable-for-keyslot linger off, or ssh-agent/ssh-add/ssh-keygen missing: no mode survives
15
+ # a reboot.
16
+ # incapable ONLY systemd-creds absent (systemd 249), or `--user` refused on
17
+ # systemd < 256, both measured: the unsealed opt-in is admissible.
18
+ # capable a byte-equal `systemd-creds --user` round trip, with linger on.
19
+ # Exit 0 capable, 2 for each other outcome (the JSON's `outcome` says which), 1 on a usage or
20
+ # seam error. Never 0 without a byte-equal round trip. dev0's manager reads `degraded` and is
21
+ # capable. `machine_id` is the opt-in record's host comparand, sha256(/etc/machine-id)[:16].
22
+ set -euo pipefail
23
+ # The measured refusal is matched as text, so every child speaks the C locale (review round 2, N4).
24
+ export LC_ALL=C
25
+
26
+ here=${BASH_SOURCE[0]%/*}
27
+ [[ $here == "${BASH_SOURCE[0]}" ]] && here=.
28
+ # shellcheck source=lib.sh
29
+ . "$here/lib.sh"
30
+
31
+ json=no
32
+ for arg in "$@"; do
33
+ case $arg in
34
+ --json) json=yes ;;
35
+ *) keyseal_die "usage: probe.sh [--json]" ;;
36
+ esac
37
+ done
38
+ keyseal_resolve_bins || exit 1
39
+
40
+ linger=$(keyseal_linger)
41
+ manager_state=$("$SYSTEMCTL" --user is-system-running 2>/dev/null) || true
42
+ manager_state=${manager_state:-unreachable}
43
+
44
+ bins_json=
45
+ declare -A present
46
+ for b in "$CREDS" "$SSH_ADD" "$SLEEP" "$SYSTEMCTL" "$LOGINCTL" "$FLOCK" "$KEYSEAL_BIN_SSH_AGENT" "$SSH_KEYGEN"; do
47
+ if [[ -x $b ]]; then present[$b]=true; else present[$b]=false; fi
48
+ bins_json+="${bins_json:+,}$(keyseal_json_str "$b"):${present[$b]}"
49
+ done
50
+
51
+ # The systemd version, as systemd-creds reports it. Empty when absent or unreadable.
52
+ systemd_version=
53
+ if [[ ${present[$CREDS]} == true ]]; then
54
+ v=$("$CREDS" --version 2>/dev/null) || v=
55
+ v=${v%%$'\n'*}
56
+ # Unanchored (review round 1, Gemini S2): a distro prefix before "systemd N" still parses.
57
+ [[ $v =~ (^|[^A-Za-z0-9_-])systemd\ ([0-9]+)([^0-9]|$) ]] && systemd_version=${BASH_REMATCH[2]}
58
+ fi
59
+
60
+ round_trip=not_run
61
+ user_refused=no
62
+ dir=
63
+ cleanup() { if [[ -n $dir ]]; then rm -rf -- "$dir"; fi; }
64
+ trap cleanup EXIT
65
+ if [[ ${present[$CREDS]} == true && -n ${XDG_RUNTIME_DIR-} && -d $XDG_RUNTIME_DIR ]] &&
66
+ dir=$(mktemp -d "$XDG_RUNTIME_DIR/keyseal-probe.XXXXXX"); then
67
+ round_trip=failed
68
+ canary=$(head -c 32 /dev/urandom | od -An -tx1 | tr -d ' \n')
69
+ name="keyseal-probe-$(head -c 6 /dev/urandom | od -An -tx1 | tr -d ' \n')"
70
+ if enc_err=$(printf '%s' "$canary" | "$CREDS" --user encrypt --name="$name" - "$dir/canary.cred" 2>&1 >/dev/null); then
71
+ if got=$("$CREDS" --user decrypt --name="$name" "$dir/canary.cred" - 2>/dev/null | sha256sum); then
72
+ if [[ ${got%% *} == "$(keyseal_sha256_str "$canary")" ]]; then round_trip=equal; else round_trip=mismatch; fi
73
+ fi
74
+ else
75
+ # The measured refusal (systemd < 256), not merely any failure (review round 1, Opus S4).
76
+ if [[ $enc_err == *"unrecognized option '--user'"* || $enc_err == *'unrecognized option "--user"'* ]]; then user_refused=yes; fi
77
+ fi
78
+ fi
79
+ cleanup
80
+ dir=
81
+
82
+ outcome= reason=
83
+ if [[ -z ${XDG_RUNTIME_DIR-} || ! -d $XDG_RUNTIME_DIR ]]; then
84
+ outcome=error reason=no_runtime_dir
85
+ elif [[ $manager_state != running && $manager_state != degraded ]]; then
86
+ outcome=error reason="manager_$manager_state"
87
+ elif [[ $linger != yes && $linger != no ]]; then
88
+ outcome=error reason=linger_unreadable
89
+ elif [[ ${present[$SYSTEMCTL]} != true || ${present[$LOGINCTL]} != true || ${present[$FLOCK]} != true || ${present[$SLEEP]} != true ]]; then
90
+ outcome=error reason=operator_binary_missing
91
+ elif [[ ${present[$CREDS]} != true ]]; then
92
+ outcome=incapable reason=systemd_creds_absent
93
+ elif [[ -z $systemd_version ]]; then
94
+ outcome=error reason=systemd_version_unreadable
95
+ elif [[ $round_trip == equal ]]; then
96
+ outcome=capable reason=round_trip_equal
97
+ elif ((systemd_version < 256)) && [[ $round_trip == failed && $user_refused == yes ]]; then
98
+ outcome=incapable reason=user_scope_refused
99
+ else
100
+ # Every other non-equal round trip is an error, never a reason to accept an unsealed key: any
101
+ # failure on systemd >= 256 (expected to seal at user scope), and below 256 anything but the
102
+ # measured `--user` refusal (bytes that came back different, or another failure).
103
+ outcome=error reason="round_trip_$round_trip"
104
+ fi
105
+ if [[ $outcome == capable || $outcome == incapable ]]; then
106
+ if [[ $linger != yes ]]; then
107
+ outcome=incapable-for-keyslot reason="linger_off (fix: loginctl enable-linger $(id -un))"
108
+ elif [[ ${present[$SSH_ADD]} != true || ${present[$KEYSEAL_BIN_SSH_AGENT]} != true || ${present[$SSH_KEYGEN]} != true ]]; then
109
+ # ssh-keygen: the loader derives an unsealed key's public key with it before loading.
110
+ outcome=incapable-for-keyslot reason="ssh_agent_or_ssh_add_missing (fix: install openssh-client)"
111
+ fi
112
+ fi
113
+
114
+ capable=false
115
+ rc=2
116
+ if [[ $outcome == capable ]]; then
117
+ capable=true
118
+ rc=0
119
+ fi
120
+ machine_id=$(keyseal_machine_id) || machine_id=
121
+ mid_json=null
122
+ [[ -n $machine_id ]] && mid_json=$(keyseal_json_str "$machine_id")
123
+ ver_json=null
124
+ [[ -n $systemd_version ]] && ver_json=$systemd_version
125
+
126
+ if [[ $json == yes ]]; then
127
+ printf '{"tool":"probe","seam":%s,"host":%s,"uid":%s,"machine_id":%s,"linger":%s,"manager_state":%s,"systemd_version":%s,"binaries":{%s},"round_trip":%s,"outcome":%s,"reason":%s,"capable":%s,"exit":%s}\n' \
128
+ "$(keyseal_seam_json)" "$(keyseal_json_str "$(uname -n)")" "$(keyseal_uid)" "$mid_json" "$(keyseal_json_str "$linger")" \
129
+ "$(keyseal_json_str "$manager_state")" "$ver_json" "$bins_json" "$(keyseal_json_str "$round_trip")" \
130
+ "$(keyseal_json_str "$outcome")" "$(keyseal_json_str "$reason")" "$capable" "$rc"
131
+ else
132
+ printf 'probe: outcome=%s reason=%s round_trip=%s linger=%s manager=%s systemd=%s seam=%s\n' \
133
+ "$outcome" "$reason" "$round_trip" "$linger" "$manager_state" "${systemd_version:-?}" "$(keyseal_seam_json)"
134
+ fi
135
+ exit "$rc"