@fased/fased 0.1.68 → 0.1.69

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 (103) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +20 -22
  3. package/config/sat-runtime.env +6 -2
  4. package/config/signer-protocol-v2.json +6 -0
  5. package/dist/agents/tools/wallet-action-tool.js +2 -0
  6. package/dist/agents/tools/wallet-tool.js +5 -3
  7. package/dist/brand.js +1 -1
  8. package/dist/build-info.json +3 -3
  9. package/dist/canvas-host/a2ui/.bundle.hash +1 -1
  10. package/dist/cli/program/register.wallet.js +88 -5
  11. package/dist/commands/wallet.js +842 -57
  12. package/dist/config/sat-runtime-ids.js +6 -1
  13. package/dist/control-ui/assets/{app-DyPTS0oM.js → app-BLFLoFPk.js} +8 -8
  14. package/dist/control-ui/assets/{federation-DgA9e-Pn.js → federation-SGMAnM_8.js} +2 -2
  15. package/dist/control-ui/assets/{index-Cu13yYHr.js → index-S88kp5ZQ.js} +3 -3
  16. package/dist/control-ui/assets/{mining-Dg-3ll0F.js → mining-ClI8RW4u.js} +2 -2
  17. package/dist/control-ui/assets/{wallet-ByKBcYhm.js → wallet-CRIhDQbW.js} +201 -116
  18. package/dist/control-ui/index.html +1 -1
  19. package/dist/control-ui/version.json +1 -1
  20. package/dist/gateway/server-http.js +46 -9
  21. package/dist/mining/mainnet-sync.js +67 -5
  22. package/dist/operator/operator-readiness.js +1 -1
  23. package/dist/plugin-sdk/{audio-preflight-_FVQjR1r.js → audio-preflight-CS7Q_7-R.js} +2 -2
  24. package/dist/plugin-sdk/channel-plugin-common.d.ts +2 -2
  25. package/dist/plugin-sdk/{chunk-items-LH4uKSnh.js → chunk-items-BLBZNORg.js} +1 -1
  26. package/dist/plugin-sdk/command-status.js +2 -2
  27. package/dist/plugin-sdk/{config-schema-DjU_uD-6.js → config-schema-BiBOUW89.js} +255 -47
  28. package/dist/plugin-sdk/device-pair.d.ts +1 -1
  29. package/dist/plugin-sdk/discord.d.ts +4 -4
  30. package/dist/plugin-sdk/index.d.ts +16 -16
  31. package/dist/plugin-sdk/index.js +2 -2
  32. package/dist/plugin-sdk/{monitor-DUMMbG4h.js → monitor-WXdn4mR-.js} +4 -4
  33. package/dist/plugin-sdk/{monitor-DXIM16Hj.js → monitor-xzVAPbg2.js} +6 -6
  34. package/dist/plugin-sdk/{openresponses-http-C8lKwta1.js → openresponses-http-DnI1LFgq.js} +2 -2
  35. package/dist/plugin-sdk/{pairing-message-mKO92L-M.d.ts → pairing-message-CiTcdD6m.d.ts} +1 -1
  36. package/dist/plugin-sdk/provider-web-search-config-contract.d.ts +1 -1
  37. package/dist/plugin-sdk/{pw-ai-DJ_gTc4M.js → pw-ai-DZYLDC-S.js} +1 -1
  38. package/dist/plugin-sdk/{runtime-dependency-nup8niQL.js → runtime-dependency-DczkvPxn.js} +9 -9
  39. package/dist/plugin-sdk/sat-runtime.d.ts +89 -10
  40. package/dist/plugin-sdk/sat-runtime.js +2 -2
  41. package/dist/plugin-sdk/{slack-B-nOOLM0.js → slack-CkIRVDUK.js} +4 -4
  42. package/dist/plugin-sdk/slack.d.ts +4 -4
  43. package/dist/plugin-sdk/{sticker-cache-BLiId8Em.js → sticker-cache-Dpap5ce_.js} +2 -2
  44. package/dist/plugin-sdk/{telegram-actions-1X9A32Dn.js → telegram-actions-Cewdga8H.js} +3 -3
  45. package/dist/plugin-sdk/telegram.d.ts +4 -4
  46. package/dist/plugin-sdk/{tmp-fased-dir-BkpWlGUe.d.ts → tmp-fased-dir-BSEHc8MQ.d.ts} +2 -2
  47. package/dist/plugin-sdk/{types-DtCOO_hW.d.ts → types-BakzIzGN.d.ts} +1 -1
  48. package/dist/plugin-sdk/{types-B-zL9gSv.d.ts → types-CzF0YQqK.d.ts} +2 -2
  49. package/dist/plugin-sdk/{web-BvjNyYBz.js → web-DGWi7i3B.js} +2 -2
  50. package/dist/plugin-sdk/whatsapp.d.ts +9 -9
  51. package/dist/plugin-sdk/{zod-schema.providers-core-BCDlgtls.d.ts → zod-schema.providers-core-CIzCjXH9.d.ts} +63 -63
  52. package/dist/wallet/local-socket-signer-lifecycle.js +79 -3
  53. package/dist/wallet/local-socket-signer-protocol.js +70 -10
  54. package/dist/wallet/mining-wallet-retirement.js +256 -0
  55. package/dist/wallet/providers/local-socket-signer-adapter.js +2 -0
  56. package/dist/wallet/signer-protocol-v2.generated.js +6 -0
  57. package/dist/wallet/wallet-agent-selection.js +6 -10
  58. package/dist/wallet/wallet-provider-registry.js +70 -29
  59. package/dist/wallet/wallet-send-approvals.js +6 -2
  60. package/dist/wallet/wallet-status.js +56 -1
  61. package/dist/wizard/onboarding.js +32 -30
  62. package/dist/wizard/onboarding.wallet.js +10 -1
  63. package/extensions/acpx/package.json +1 -1
  64. package/extensions/bluebubbles/package.json +1 -1
  65. package/extensions/copilot-proxy/package.json +1 -1
  66. package/extensions/diagnostics-otel/package.json +1 -1
  67. package/extensions/fased-federation/package.json +1 -1
  68. package/extensions/google-gemini-cli-auth/package.json +1 -1
  69. package/extensions/imessage/package.json +1 -1
  70. package/extensions/irc/package.json +1 -1
  71. package/extensions/line/package.json +1 -1
  72. package/extensions/llm-task/package.json +1 -1
  73. package/extensions/lobster/package.json +1 -1
  74. package/extensions/matrix/CHANGELOG.md +6 -0
  75. package/extensions/matrix/package.json +1 -1
  76. package/extensions/mattermost/package.json +1 -1
  77. package/extensions/memory-core/package.json +2 -2
  78. package/extensions/memory-lancedb/package.json +1 -1
  79. package/extensions/minimax-portal-auth/package.json +1 -1
  80. package/extensions/msteams/CHANGELOG.md +6 -0
  81. package/extensions/msteams/package.json +1 -1
  82. package/extensions/nextcloud-talk/package.json +1 -1
  83. package/extensions/nostr/CHANGELOG.md +6 -0
  84. package/extensions/nostr/package.json +1 -1
  85. package/extensions/open-prose/package.json +1 -1
  86. package/extensions/sat-mining/package.json +1 -1
  87. package/extensions/signal/package.json +1 -1
  88. package/extensions/synology-chat/package.json +1 -1
  89. package/extensions/tlon/package.json +1 -1
  90. package/extensions/twitch/CHANGELOG.md +6 -0
  91. package/extensions/twitch/package.json +1 -1
  92. package/extensions/voice-call/CHANGELOG.md +6 -0
  93. package/extensions/voice-call/package.json +1 -1
  94. package/extensions/zalo/CHANGELOG.md +6 -0
  95. package/extensions/zalo/package.json +1 -1
  96. package/extensions/zalouser/CHANGELOG.md +6 -0
  97. package/extensions/zalouser/package.json +1 -1
  98. package/install.sh +276 -103
  99. package/package.json +4 -3
  100. package/scripts/install-fased-signerd.sh +4 -8
  101. package/scripts/install-runtime-profile.sh +27 -0
  102. package/scripts/start-managed.sh +13 -1
  103. package/scripts/fased-signer-wallet-import-hosting.sh +0 -78
package/install.sh CHANGED
@@ -36,15 +36,61 @@ if [[ "$install_entry_legacy_ts_authkey" -eq 1 ]]; then
36
36
  fi
37
37
 
38
38
  if [[ "$install_entry_is_stream" -eq 1 && "$install_entry_hosting" -eq 1 ]]; then
39
- echo "Refusing streamed VPS Hosting execution." >&2
40
- echo "Download and verify an exact tagged release installer before running --hosting or --repair-hosting." >&2
41
- exit 1
39
+ if [[ "${#install_entry_args[@]}" -ne 1 || "${install_entry_args[0]:-}" != "--hosting" ]]; then
40
+ echo "Streamed VPS Hosting accepts only the exact fresh-install selector: --hosting" >&2
41
+ echo "Use an exact tagged, attested installer for repair, release overrides, or advanced selectors." >&2
42
+ exit 1
43
+ fi
44
+ install_entry_exported_env=()
45
+ mapfile -t install_entry_exported_env < <(compgen -e)
46
+ for install_entry_env_name in "${install_entry_exported_env[@]}"; do
47
+ if [[ "$install_entry_env_name" == FASED_* ]]; then
48
+ echo "Refusing Fased environment overrides during streamed VPS Hosting: ${install_entry_env_name}" >&2
49
+ exit 1
50
+ fi
51
+ done
52
+ install_entry_unsafe_env=(
53
+ HTTP_PROXY HTTPS_PROXY ALL_PROXY NO_PROXY http_proxy https_proxy all_proxy no_proxy
54
+ CURL_HOME CURL_CA_BUNDLE SSL_CERT_FILE SSL_CERT_DIR GIT_SSL_NO_VERIFY
55
+ GH_HOST GH_REPO GH_CONFIG_DIR TMPDIR LD_PRELOAD LD_LIBRARY_PATH BASH_ENV ENV CDPATH
56
+ )
57
+ for install_entry_env_name in "${install_entry_unsafe_env[@]}"; do
58
+ if [[ -n "${!install_entry_env_name+x}" ]]; then
59
+ echo "Refusing unsafe environment override during streamed VPS Hosting: ${install_entry_env_name}" >&2
60
+ exit 1
61
+ fi
62
+ done
63
+ install_entry_existing_hosting_paths=(
64
+ /etc/fased
65
+ /opt/fased
66
+ /var/lib/fased-installer
67
+ /var/lib/fased-gateway
68
+ /var/lib/fased-signerd
69
+ /etc/systemd/system/fased-gateway.service
70
+ /etc/systemd/system/fased-signerd.service
71
+ /usr/local/libexec/fased-gateway-launch
72
+ /usr/local/libexec/fased-host-updater
73
+ )
74
+ for install_entry_existing_path in "${install_entry_existing_hosting_paths[@]}"; do
75
+ if [[ -e "$install_entry_existing_path" || -L "$install_entry_existing_path" ]]; then
76
+ echo "Streamed VPS Hosting is only for a fresh host; existing Fased state was found." >&2
77
+ echo "Use the exact tagged repair or the installed updater instead of the mutable main bootstrap." >&2
78
+ exit 1
79
+ fi
80
+ done
81
+ PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
82
+ LANG="C.UTF-8"
83
+ LC_ALL="C.UTF-8"
84
+ export PATH LANG LC_ALL
85
+ unset -f curl gh jq tar sha256sum awk stat find grep flock install mv cp chown chmod sync 2>/dev/null || true
86
+ hash -r 2>/dev/null || true
42
87
  fi
43
88
 
44
89
  # A Hosting file request enters the attest-and-extract bootstrap unless it is
45
90
  # the exact inner invocation carrying the root-owned verified bundle marker.
46
- # Streamed Hosting was rejected above; streamed execution reaches this block
47
- # only for the non-root Local bootstrap.
91
+ # Streamed Hosting reaches this block only for the exact fresh --hosting
92
+ # selector validated above. Repair and advanced Hosting selectors remain
93
+ # exact-tag-only.
48
94
  if [[ "$install_entry_is_stream" -eq 1 || \
49
95
  ( "$install_entry_hosting" -eq 1 && -z "$install_entry_verified_bundle" ) ]]; then
50
96
  install_repo_url="${FASED_INSTALL_REPO:-https://github.com/fased-ai/fased.git}"
@@ -126,7 +172,7 @@ if [[ "$install_entry_is_stream" -eq 1 || \
126
172
  local source_tmp=""
127
173
  keyring_tmp="$(mktemp)"
128
174
  source_tmp="$(mktemp)"
129
- curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg -o "$keyring_tmp"
175
+ curl -q -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg -o "$keyring_tmp"
130
176
  printf 'deb [arch=%s signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main\n' "$(dpkg --print-architecture)" >"$source_tmp"
131
177
  bootstrap_as_root install -d -m 0755 /etc/apt/keyrings
132
178
  bootstrap_as_root install -m 0644 "$keyring_tmp" /etc/apt/keyrings/githubcli-archive-keyring.gpg
@@ -160,7 +206,7 @@ if [[ "$install_entry_is_stream" -eq 1 || \
160
206
  }
161
207
 
162
208
  resolve_public_latest_release_tag() {
163
- curl -fsSL --proto '=https' --tlsv1.2 \
209
+ curl -q -fsSL --proto '=https' --tlsv1.2 \
164
210
  -H 'Accept: application/vnd.github+json' \
165
211
  -H 'X-GitHub-Api-Version: 2022-11-28' \
166
212
  -H 'User-Agent: fased-installer' \
@@ -227,22 +273,37 @@ if [[ "$install_entry_is_stream" -eq 1 || \
227
273
  fi
228
274
 
229
275
  local architecture=""
276
+ local signer_platform=""
230
277
  case "$(uname -m)" in
231
- x86_64|amd64) architecture="x64" ;;
232
- aarch64|arm64) architecture="arm64" ;;
278
+ x86_64|amd64)
279
+ architecture="x64"
280
+ signer_platform="linux-amd64"
281
+ ;;
282
+ aarch64|arm64)
283
+ architecture="arm64"
284
+ signer_platform="linux-arm64"
285
+ ;;
233
286
  *)
234
287
  echo "Unsupported Hosting architecture: $(uname -m)" >&2
235
288
  exit 1
236
289
  ;;
237
290
  esac
238
- local asset="fased-hosted-app-linux-${architecture}-v${release_version}.tar.gz"
291
+ local asset="fased-hosted-app-v2-linux-${architecture}-v${release_version}.tar.gz"
239
292
  local release_url="https://github.com/fased-ai/fased/releases/download/v${release_version}"
240
293
  local release_parent="/var/lib/fased-installer/releases/v${release_version}"
241
294
  local staging="${release_parent}/.staging.$$"
242
295
  local preflight=""
243
296
  preflight="$(mktemp -d "${TMPDIR:-/tmp}/fased-hosting-bootstrap.XXXXXX")"
244
297
  local archive="${preflight}/${asset}"
298
+ local dependency_archive=""
299
+ local dependency_asset=""
300
+ local dependency_expected=""
301
+ local dependency_hash=""
302
+ local signer_binary=""
303
+ local signer_asset=""
304
+ local signer_expected=""
245
305
  local release_manifest="${preflight}/fased-hosted-release-v2.json"
306
+ local release_manifest_bundle="${preflight}/fased-hosted-release-v2.json.attestation.json"
246
307
  local expected=""
247
308
  local actual=""
248
309
  local manifest_digest=""
@@ -250,16 +311,43 @@ if [[ "$install_entry_is_stream" -eq 1 || \
250
311
  local manifest_signer_commit=""
251
312
 
252
313
  umask 077
253
- trap 'rm -rf -- "${preflight:-}" "${staging:-}"' EXIT
254
- curl -fL --proto '=https' --tlsv1.2 "$release_url/fased-hosted-release-v2.json" -o "$release_manifest"
314
+ hosting_bootstrap_cleanup() {
315
+ local status="$?"
316
+ rm -rf -- "${preflight:-}" "${staging:-}"
317
+ if [[ "$status" -ne 0 ]]; then
318
+ if [[ -e /var/lib/fased-installer || -L /var/lib/fased-installer ]]; then
319
+ echo "Hosting bootstrap stopped without activating Fased services." >&2
320
+ echo "Persistent installer state exists; retry only with the exact tagged, attested repair procedure." >&2
321
+ else
322
+ echo "Hosting bootstrap stopped before persistent Fased state was created; fix the reported problem and rerun the exact --hosting command." >&2
323
+ fi
324
+ fi
325
+ return "$status"
326
+ }
327
+ trap hosting_bootstrap_cleanup EXIT
328
+ curl -q -fL --proto '=https' --tlsv1.2 "$release_url/fased-hosted-release-v2.json" -o "$release_manifest"
329
+ curl -q -fL --proto '=https' --tlsv1.2 "$release_url/fased-hosted-release-v2.json.attestation.json" -o "$release_manifest_bundle"
255
330
  GH_PROMPT_DISABLED=1 gh attestation verify "$release_manifest" \
256
331
  --repo fased-ai/fased \
332
+ --bundle "$release_manifest_bundle" \
257
333
  --signer-workflow fased-ai/fased/.github/workflows/hosted-runtime-release.yml \
258
334
  --source-ref "refs/tags/v${release_version}" \
259
335
  --deny-self-hosted-runners >/dev/null
260
336
  local manifest_selection=""
261
- manifest_selection="$(jq -er --arg version "$release_version" --arg architecture "$architecture" '
262
- if .schemaVersion == 2 and
337
+ manifest_selection="$(jq -er --arg version "$release_version" --arg architecture "$architecture" --arg signer_platform "$signer_platform" '
338
+ if (keys == ["application", "release", "schemaVersion", "signer"]) and
339
+ .schemaVersion == 2 and
340
+ (.release | keys == ["commit", "tag", "version"]) and
341
+ (.application | keys == ["linux"]) and
342
+ (.application.linux | keys == ["arm64", "x64"]) and
343
+ (.application.linux[$architecture] | keys == ["artifact", "dependencies"]) and
344
+ (.application.linux[$architecture].artifact | keys == ["asset", "sha256"]) and
345
+ (.application.linux[$architecture].dependencies | keys == ["asset", "dependencyHash", "sha256"]) and
346
+ (.signer | keys == ["capabilities", "capabilitiesDigest", "platforms", "release"]) and
347
+ (.signer.release | keys == ["buildInputDigest", "commit", "development", "version"]) and
348
+ (.signer.platforms | keys == ["darwin-amd64", "darwin-arm64", "linux-amd64", "linux-arm64"]) and
349
+ (.signer.platforms[$signer_platform] | keys == ["asset", "sha256"]) and
350
+ (.signer.capabilitiesDigest | test("^sha256:[a-f0-9]{64}$")) and
263
351
  (.release.version == $version) and (.release.tag == ("v" + $version)) and
264
352
  (.release.commit | test("^[a-f0-9]{40}$")) and
265
353
  (.signer.release.version == $version) and
@@ -267,38 +355,107 @@ if [[ "$install_entry_is_stream" -eq 1 || \
267
355
  (.signer.release.development == false) and
268
356
  (.signer.release.buildInputDigest | test("^sha256:[a-f0-9]{64}$")) and
269
357
  (.application.linux[$architecture].artifact.asset | test("^[A-Za-z0-9][A-Za-z0-9._-]+$")) and
270
- (.application.linux[$architecture].artifact.sha256 | test("^[a-f0-9]{64}$"))
358
+ (.application.linux[$architecture].artifact.sha256 | test("^[a-f0-9]{64}$")) and
359
+ (.application.linux[$architecture].dependencies.asset | test("^[A-Za-z0-9][A-Za-z0-9._-]+$")) and
360
+ (.application.linux[$architecture].dependencies.sha256 | test("^[a-f0-9]{64}$")) and
361
+ (.application.linux[$architecture].dependencies.dependencyHash | test("^[a-f0-9]{64}$")) and
362
+ (.signer.platforms[$signer_platform].asset | test("^[A-Za-z0-9][A-Za-z0-9._-]+$")) and
363
+ (.signer.platforms[$signer_platform].sha256 | test("^[a-f0-9]{64}$"))
271
364
  then [
272
365
  .release.commit,
273
366
  .signer.release.commit,
274
367
  .application.linux[$architecture].artifact.asset,
275
- .application.linux[$architecture].artifact.sha256
368
+ .application.linux[$architecture].artifact.sha256,
369
+ .application.linux[$architecture].dependencies.asset,
370
+ .application.linux[$architecture].dependencies.sha256,
371
+ .application.linux[$architecture].dependencies.dependencyHash,
372
+ .signer.platforms[$signer_platform].asset,
373
+ .signer.platforms[$signer_platform].sha256
276
374
  ] | @tsv
277
375
  else error("invalid hosted release manifest") end
278
376
  ' "$release_manifest")" || {
279
377
  echo "Hosted release manifest does not bind this exact app and signer release." >&2
280
378
  exit 1
281
379
  }
282
- IFS=$'\t' read -r manifest_commit manifest_signer_commit asset expected <<<"$manifest_selection"
283
- [[ "$manifest_commit" == "$manifest_signer_commit" && "$asset" == "fased-hosted-app-linux-${architecture}-v${release_version}.tar.gz" ]] || {
380
+ IFS=$'\t' read -r manifest_commit manifest_signer_commit asset expected dependency_asset dependency_expected dependency_hash signer_asset signer_expected <<<"$manifest_selection"
381
+ [[ "$manifest_commit" == "$manifest_signer_commit" && \
382
+ "$asset" == "fased-hosted-app-v2-linux-${architecture}-v${release_version}.tar.gz" && \
383
+ "$dependency_asset" == "fased-hosted-deps-linux-${architecture}-${dependency_hash}.tar.gz" && \
384
+ "$signer_asset" == "fased-signerd-${signer_platform}" ]] || {
284
385
  echo "Hosted release manifest selects a mixed commit or unexpected app artifact." >&2
285
386
  exit 1
286
387
  }
287
388
  archive="${preflight}/${asset}"
288
- curl -fL --proto '=https' --tlsv1.2 "$release_url/$asset" -o "$archive"
389
+ dependency_archive="${preflight}/${dependency_asset}"
390
+ signer_binary="${preflight}/${signer_asset}"
391
+ curl -q -fL --proto '=https' --tlsv1.2 "$release_url/$asset" -o "$archive"
392
+ curl -q -fL --proto '=https' --tlsv1.2 "$release_url/$dependency_asset" -o "$dependency_archive"
393
+ curl -q -fL --proto '=https' --tlsv1.2 "$release_url/$signer_asset" -o "$signer_binary"
289
394
  actual="$(sha256sum "$archive" | awk '{print tolower($1)}')"
395
+ local dependency_actual=""
396
+ local signer_actual=""
397
+ dependency_actual="$(sha256sum "$dependency_archive" | awk '{print tolower($1)}')"
398
+ signer_actual="$(sha256sum "$signer_binary" | awk '{print tolower($1)}')"
290
399
  manifest_digest="$(sha256sum "$release_manifest" | awk '{print tolower($1)}')"
291
- if [[ ! "$expected" =~ ^[a-f0-9]{64}$ || "$actual" != "$expected" ]]; then
292
- echo "Hosted release checksum verification failed." >&2
400
+ if [[ ! "$expected" =~ ^[a-f0-9]{64}$ || "$actual" != "$expected" || \
401
+ ! "$dependency_expected" =~ ^[a-f0-9]{64}$ || "$dependency_actual" != "$dependency_expected" || \
402
+ ! "$signer_expected" =~ ^[a-f0-9]{64}$ || "$signer_actual" != "$signer_expected" ]]; then
403
+ echo "Hosted app, dependency, or signer release checksum verification failed." >&2
293
404
  exit 1
294
405
  fi
295
- GH_PROMPT_DISABLED=1 gh attestation verify "$archive" \
296
- --repo fased-ai/fased \
297
- --signer-workflow fased-ai/fased/.github/workflows/hosted-runtime-release.yml \
298
- --source-ref "refs/tags/v${release_version}" \
299
- --deny-self-hosted-runners >/dev/null
300
- # Only after the exact unified manifest and selected app artifact are both
301
- # attested and digest-bound may the bootstrap create persistent root state.
406
+ local entry=""
407
+ while IFS= read -r entry; do
408
+ entry="${entry%/}"
409
+ if [[ -z "$entry" || "$entry" == /* || "$entry" == *\\* || \
410
+ ( "$entry" != "package" && "$entry" != package/* ) || \
411
+ "$entry" == *"/../"* || "$entry" == ../* || "$entry" == */.. || \
412
+ "$entry" == *"/./"* || "$entry" == ./* || "$entry" == */. ]]; then
413
+ echo "Hosted app archive contains an unsafe path: $entry" >&2
414
+ exit 1
415
+ fi
416
+ done < <(tar -tzf "$archive")
417
+ while IFS= read -r entry; do
418
+ entry="${entry%/}"
419
+ if [[ -z "$entry" || "$entry" == /* || "$entry" == *\\* || \
420
+ ( "$entry" != "node_modules" && "$entry" != node_modules/* ) || \
421
+ "$entry" == *"/../"* || "$entry" == ../* || "$entry" == */.. || \
422
+ "$entry" == *"/./"* || "$entry" == ./* || "$entry" == */. ]]; then
423
+ echo "Hosted dependency archive contains an unsafe path: $entry" >&2
424
+ exit 1
425
+ fi
426
+ done < <(tar -tzf "$dependency_archive")
427
+
428
+ local verified_extract="$preflight/verified-app"
429
+ install -d -m 0700 "$verified_extract"
430
+ tar -xzf "$archive" -C "$verified_extract" --no-same-owner --no-same-permissions
431
+ local verified_package_root="$verified_extract/package"
432
+ [[ -f "$verified_package_root/install.sh" && -f "$verified_package_root/package.json" && \
433
+ ! -L "$verified_package_root/install.sh" && -f "$verified_package_root/dist/build-info.json" && \
434
+ ! -L "$verified_package_root/dist/build-info.json" ]] || {
435
+ echo "Attested Hosting bundle is incomplete or has an invalid entrypoint." >&2
436
+ exit 1
437
+ }
438
+ local packaged_version=""
439
+ local packaged_commit=""
440
+ local build_info_version=""
441
+ packaged_version="$(awk -F'"' '/^[[:space:]]*"version"[[:space:]]*:/ { print $4; exit }' "$verified_package_root/package.json")"
442
+ packaged_commit="$(awk -F'"' '/^[[:space:]]*"commit"[[:space:]]*:/ { print $4; exit }' "$verified_package_root/dist/build-info.json")"
443
+ build_info_version="$(awk -F'"' '/^[[:space:]]*"version"[[:space:]]*:/ { print $4; exit }' "$verified_package_root/dist/build-info.json")"
444
+ [[ "$packaged_version" == "$release_version" && "$build_info_version" == "$release_version" && \
445
+ "$packaged_commit" =~ ^[a-f0-9]{40}$ && "$packaged_commit" == "$manifest_commit" ]] || {
446
+ echo "Attested Hosting application identity does not match the unified release manifest." >&2
447
+ exit 1
448
+ }
449
+ if find "$verified_package_root" -xdev ! -type f ! -type d -print -quit | grep -q . || \
450
+ find "$verified_package_root" -xdev -type f -links +1 -print -quit | grep -q . || \
451
+ find "$verified_package_root" -xdev \( ! -user root -o -perm /022 \) -print -quit | grep -q .; then
452
+ echo "Attested Hosting bundle violates the file, link, ownership, or writable-mode policy." >&2
453
+ exit 1
454
+ fi
455
+
456
+ # Only after the exact unified manifest, app/dependency layers, signer
457
+ # binary, archive layout, and build identity are verified may the
458
+ # bootstrap create persistent Fased root state.
302
459
  install -d -m 0700 -o root -g root /var/lib/fased-installer /var/lib/fased-installer/releases "$release_parent"
303
460
  exec 9>/var/lib/fased-installer/install.lock
304
461
  chmod 0600 /var/lib/fased-installer/install.lock
@@ -337,52 +494,9 @@ if [[ "$install_entry_is_stream" -eq 1 || \
337
494
  exit 1
338
495
  fi
339
496
 
340
- local entry=""
341
- while IFS= read -r entry; do
342
- entry="${entry%/}"
343
- if [[ -z "$entry" || "$entry" == /* || "$entry" == *\\* || \
344
- ( "$entry" != "package" && "$entry" != package/* ) || \
345
- "$entry" == *"/../"* || "$entry" == ../* || "$entry" == */.. || \
346
- "$entry" == *"/./"* || "$entry" == ./* || "$entry" == */. ]]; then
347
- echo "Hosted release archive contains an unsafe path: $entry" >&2
348
- exit 1
349
- fi
350
- done < <(tar -tzf "$archive")
351
-
352
497
  install -d -m 0700 -o root -g root "$staging/extract"
353
- tar -xzf "$archive" -C "$staging/extract" --no-same-owner --no-same-permissions
498
+ cp -a "$verified_package_root" "$staging/extract/package"
354
499
  local package_root="$staging/extract/package"
355
- [[ -f "$package_root/install.sh" && -f "$package_root/package.json" && ! -L "$package_root/install.sh" ]] || {
356
- echo "Attested Hosting bundle is incomplete." >&2
357
- exit 1
358
- }
359
- local packaged_version=""
360
- packaged_version="$(awk -F'"' '/^[[:space:]]*"version"[[:space:]]*:/ { print $4; exit }' "$package_root/package.json")"
361
- [[ "$packaged_version" == "$release_version" ]] || {
362
- echo "Attested Hosting bundle version does not match v${release_version}." >&2
363
- exit 1
364
- }
365
- [[ -f "$package_root/dist/build-info.json" && ! -L "$package_root/dist/build-info.json" ]] || {
366
- echo "Attested Hosting bundle is missing immutable build identity." >&2
367
- exit 1
368
- }
369
- local packaged_commit=""
370
- local build_info_version=""
371
- packaged_commit="$(awk -F'"' '/^[[:space:]]*"commit"[[:space:]]*:/ { print $4; exit }' "$package_root/dist/build-info.json")"
372
- build_info_version="$(awk -F'"' '/^[[:space:]]*"version"[[:space:]]*:/ { print $4; exit }' "$package_root/dist/build-info.json")"
373
- [[ "$packaged_commit" =~ ^[a-f0-9]{40}$ && "$build_info_version" == "$release_version" ]] || {
374
- echo "Attested Hosting bundle build identity is invalid or does not match v${release_version}." >&2
375
- exit 1
376
- }
377
- [[ "$packaged_commit" == "$manifest_commit" ]] || {
378
- echo "Attested Hosting application commit does not match the unified release manifest." >&2
379
- exit 1
380
- }
381
- if find "$package_root" -xdev ! -type f ! -type d -print -quit | grep -q . || \
382
- find "$package_root" -xdev -type f -links +1 -print -quit | grep -q .; then
383
- echo "Attested Hosting bundle contains a symlink, special file, or hardlinked regular file." >&2
384
- exit 1
385
- fi
386
500
  chown -R root:root "$staging"
387
501
  chmod -R go-w "$staging"
388
502
  if find "$staging" -xdev \( ! -user root -o -perm /022 \) -print -quit | grep -q .; then
@@ -3958,14 +4072,22 @@ EOF
3958
4072
 
3959
4073
  install_fixed_host_gateway_service() {
3960
4074
  local target_user="${FASED_INSTALL_USER:-app}"
4075
+ local gateway_user="${FASED_GATEWAY_USER:-fased-gateway}"
4076
+ local gateway_group="${FASED_GATEWAY_GROUP:-fased-gateway}"
4077
+ local config_group="${FASED_CONFIG_GROUP:-fased-config}"
3961
4078
  local target_home
3962
4079
  target_home="$(getent passwd "$target_user" | cut -d: -f6)"
3963
4080
  [[ -n "$target_home" ]] || target_home="/home/$target_user"
3964
4081
  local target_repo_dir="${1:-$target_home/fased}"
3965
- [[ "$target_user" != "root" && "$target_user" =~ ^[A-Za-z0-9_.@-]+$ ]] || {
3966
- echo "Invalid hosted Gateway app account." >&2
4082
+ [[ "$target_user" != "root" && "$target_user" != "$gateway_user" && "$target_user" =~ ^[A-Za-z0-9_.@-]+$ && "$gateway_user" =~ ^[A-Za-z0-9_.@-]+$ ]] || {
4083
+ echo "Invalid or overlapping hosted operator and Gateway accounts." >&2
3967
4084
  exit 1
3968
4085
  }
4086
+
4087
+ install -d -m 2770 -o "$target_user" -g "$config_group" "${target_home}/.fased"
4088
+ chgrp -R "$config_group" "${target_home}/.fased"
4089
+ chmod -R g+rwX,o-rwx "${target_home}/.fased"
4090
+ find "${target_home}/.fased" -type d -exec chmod g+s {} +
3969
4091
  [[ "$target_repo_dir" == "$target_home/"* && "$target_repo_dir" =~ ^/[A-Za-z0-9_./@-]+$ ]] || {
3970
4092
  echo "Hosted Gateway checkout must remain below the app account home." >&2
3971
4093
  exit 1
@@ -3983,8 +4105,9 @@ EOF
3983
4105
  chown root:root /usr/local/libexec/fased-gateway-launch
3984
4106
  chmod 0755 /usr/local/libexec/fased-gateway-launch
3985
4107
 
3986
- # The root-owned unit has fixed inputs. It may execute app-owned application code only as
3987
- # the app account; no app-supplied unit text or privileged lifecycle hook is accepted.
4108
+ # The root-owned unit has fixed inputs. The non-login Gateway identity can read the
4109
+ # operator-installed runtime and share application configuration through fased-config,
4110
+ # but it is not a member of the restricted signer-operator socket group.
3988
4111
  cat >/etc/systemd/system/fased-gateway.service <<EOF
3989
4112
  [Unit]
3990
4113
  Description=Fased Gateway (managed)
@@ -3993,19 +4116,21 @@ Wants=fased-signerd.service network-online.target
3993
4116
 
3994
4117
  [Service]
3995
4118
  Type=simple
3996
- User=${target_user}
3997
- Group=${target_user}
4119
+ User=${gateway_user}
4120
+ Group=${gateway_group}
4121
+ SupplementaryGroups=${config_group}
3998
4122
  WorkingDirectory=${target_repo_dir}
3999
4123
  Environment=HOME=${target_home}
4000
4124
  Environment=FASED_GATEWAY_MODE=managed
4001
4125
  Environment=FASED_MANAGED_INTERNAL=1
4126
+ Environment=FASED_GATEWAY_SERVICE=1
4002
4127
  Environment=FASED_GATEWAY_PORT=18789
4003
4128
  Environment=FASED_HOST_PROFILE=hosting
4004
4129
  Environment=FASED_WALLET_LOCAL_SIGNER_SOCKET=/run/fased-signerd/app.sock
4005
4130
  ExecStart=/usr/local/libexec/fased-gateway-launch
4006
4131
  Restart=always
4007
4132
  RestartSec=5
4008
- UMask=0077
4133
+ UMask=0007
4009
4134
  NoNewPrivileges=true
4010
4135
  PrivateTmp=true
4011
4136
  PrivateDevices=true
@@ -4216,8 +4341,15 @@ EOF
4216
4341
 
4217
4342
  ensure_host_boundary_accounts() {
4218
4343
  local target_user="${FASED_INSTALL_USER:-app}"
4344
+ local gateway_user="${FASED_GATEWAY_USER:-fased-gateway}"
4219
4345
  local signer_user="${FASED_SIGNER_USER:-fased-signer}"
4220
4346
  local gateway_group="${FASED_GATEWAY_GROUP:-fased-gateway}"
4347
+ local operator_group="${FASED_OPERATOR_GROUP:-fased-operator}"
4348
+ local config_group="${FASED_CONFIG_GROUP:-fased-config}"
4349
+ [[ "$target_user" != "$gateway_user" && "$target_user" != "$signer_user" && "$gateway_user" != "$signer_user" ]] || {
4350
+ echo "Hosting operator, Gateway, and signer accounts must be distinct." >&2
4351
+ exit 1
4352
+ }
4221
4353
  if ! id -u "$target_user" >/dev/null 2>&1; then
4222
4354
  if need_cmd useradd; then
4223
4355
  useradd -m -s /bin/bash "$target_user"
@@ -4226,6 +4358,15 @@ ensure_host_boundary_accounts() {
4226
4358
  fi
4227
4359
  fi
4228
4360
  getent group "$gateway_group" >/dev/null 2>&1 || groupadd --system "$gateway_group"
4361
+ getent group "$operator_group" >/dev/null 2>&1 || groupadd --system "$operator_group"
4362
+ getent group "$config_group" >/dev/null 2>&1 || groupadd --system "$config_group"
4363
+ if ! id -u "$gateway_user" >/dev/null 2>&1; then
4364
+ if need_cmd useradd; then
4365
+ useradd --system --gid "$gateway_group" --home-dir /var/lib/fased-gateway --shell /usr/sbin/nologin "$gateway_user"
4366
+ else
4367
+ adduser --system --ingroup "$gateway_group" --home /var/lib/fased-gateway --shell /usr/sbin/nologin "$gateway_user"
4368
+ fi
4369
+ fi
4229
4370
  if ! id -u "$signer_user" >/dev/null 2>&1; then
4230
4371
  if need_cmd useradd; then
4231
4372
  useradd --system --home-dir /var/lib/fased-signerd --shell /usr/sbin/nologin "$signer_user"
@@ -4233,8 +4374,13 @@ ensure_host_boundary_accounts() {
4233
4374
  adduser --system --home /var/lib/fased-signerd --shell /usr/sbin/nologin "$signer_user"
4234
4375
  fi
4235
4376
  fi
4377
+ usermod -g "$gateway_group" -s /usr/sbin/nologin "$gateway_user"
4378
+ passwd -l "$gateway_user" >/dev/null 2>&1 || true
4236
4379
  passwd -l "$signer_user" >/dev/null 2>&1 || true
4237
- usermod -aG "$gateway_group" "$target_user"
4380
+ usermod -aG "$config_group" "$gateway_user"
4381
+ usermod -aG "$operator_group,$config_group" "$target_user"
4382
+ gpasswd -d "$target_user" "$gateway_group" >/dev/null 2>&1 || true
4383
+ gpasswd -d "$gateway_user" "$operator_group" >/dev/null 2>&1 || true
4238
4384
  for admin_group in sudo wheel; do
4239
4385
  if getent group "$admin_group" >/dev/null 2>&1; then
4240
4386
  gpasswd -d "$target_user" "$admin_group" >/dev/null 2>&1 || true
@@ -4254,14 +4400,22 @@ ensure_host_boundary_accounts() {
4254
4400
 
4255
4401
  install_host_signer_and_updater_services() {
4256
4402
  local target_user="${FASED_INSTALL_USER:-app}"
4403
+ local gateway_user="${FASED_GATEWAY_USER:-fased-gateway}"
4257
4404
  local signer_user="${FASED_SIGNER_USER:-fased-signer}"
4258
4405
  local gateway_group="${FASED_GATEWAY_GROUP:-fased-gateway}"
4406
+ local operator_group="${FASED_OPERATOR_GROUP:-fased-operator}"
4259
4407
  local gateway_gid
4408
+ local gateway_uid
4409
+ local operator_uid
4410
+ local signer_uid
4260
4411
  local version
4261
4412
  gateway_gid="$(getent group "$gateway_group" | cut -d: -f3)"
4413
+ gateway_uid="$(id -u "$gateway_user")"
4414
+ operator_uid="$(id -u "$target_user")"
4415
+ signer_uid="$(id -u "$signer_user")"
4262
4416
  version="$(node -p "require(process.argv[1]).version" "$FASED_DIR/package.json" 2>/dev/null || true)"
4263
- [[ "$gateway_gid" =~ ^[0-9]+$ && "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z.-]+)?$ ]] || {
4264
- echo "Could not resolve hosted signer group or release version." >&2
4417
+ [[ "$gateway_gid" =~ ^[0-9]+$ && "$gateway_uid" =~ ^[0-9]+$ && "$operator_uid" =~ ^[0-9]+$ && "$signer_uid" =~ ^[0-9]+$ && "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z.-]+)?$ ]] || {
4418
+ echo "Could not resolve hosted signer principals, socket group, or release version." >&2
4265
4419
  exit 1
4266
4420
  }
4267
4421
  if [[ -n "${FASED_HOST_SIGNER_BINARY:-}" ]]; then
@@ -4269,6 +4423,19 @@ install_host_signer_and_updater_services() {
4269
4423
  echo "The root updater must fetch and attest the exact tagged signer artifact." >&2
4270
4424
  exit 1
4271
4425
  fi
4426
+ local sat_runtime_value=""
4427
+ if [[ -n "${FASED_SAT_PROGRAM_ID:-}${FASED_SAT_BOND_PROGRAM_ID:-}${FASED_SAT_MINT_ADDRESS:-}${FASED_SAT_MINT_PROGRAM_ID:-}" ]]; then
4428
+ for sat_runtime_value in \
4429
+ "${FASED_SAT_PROGRAM_ID:-}" \
4430
+ "${FASED_SAT_BOND_PROGRAM_ID:-}" \
4431
+ "${FASED_SAT_MINT_ADDRESS:-}" \
4432
+ "${FASED_SAT_MINT_PROGRAM_ID:-}"; do
4433
+ [[ "$sat_runtime_value" =~ ^[1-9A-HJ-NP-Za-km-z]{32,44}$ ]] || {
4434
+ echo "Hosted signer SAT runtime requires one complete valid signed-manifest ID tuple." >&2
4435
+ exit 1
4436
+ }
4437
+ done
4438
+ fi
4272
4439
  local existing_signer_dropins
4273
4440
  existing_signer_dropins="$(systemctl show fased-signerd.service --property=DropInPaths --value 2>/dev/null || true)"
4274
4441
  if [[ -n "$existing_signer_dropins" ]]; then
@@ -4285,6 +4452,7 @@ install_host_signer_and_updater_services() {
4285
4452
  install -m 0755 -o root -g root "$FASED_DIR/scripts/fased-host-updater.mjs" /usr/local/libexec/fased-host-updater.mjs
4286
4453
  install -m 0755 -o root -g root "$FASED_DIR/scripts/fased-host-updaterctl.mjs" /usr/local/libexec/fased-host-updaterctl.mjs
4287
4454
  rm -f /usr/local/libexec/fased-host-bootstrapd.mjs /usr/local/libexec/fased-host-bootstrapctl.mjs
4455
+ rm -f /usr/local/sbin/fased-signer-wallet-import
4288
4456
  rm -rf /run/fased-host-bootstrap
4289
4457
  rm -f /var/log/fased-host-bootstrap.log
4290
4458
  install -m 0755 -o root -g root "$FASED_DIR/scripts/migrate-hosted-signer-v2.mjs" /usr/local/libexec/migrate-hosted-signer-v2.mjs
@@ -4292,7 +4460,6 @@ install_host_signer_and_updater_services() {
4292
4460
  install -m 0755 -o root -g root "$FASED_DIR/scripts/fased-signer-enroll-hosting.sh" /usr/local/sbin/fased-signer-enroll
4293
4461
  install -m 0755 -o root -g root "$FASED_DIR/scripts/fased-signer-policy-hosting.sh" /usr/local/sbin/fased-signer-policy
4294
4462
  install -m 0755 -o root -g root "$FASED_DIR/scripts/fased-signer-network-hosting.sh" /usr/local/sbin/fased-signer-network
4295
- install -m 0755 -o root -g root "$FASED_DIR/scripts/fased-signer-wallet-import-hosting.sh" /usr/local/sbin/fased-signer-wallet-import
4296
4463
  install -m 0644 -o root -g root "$FASED_DIR/config/signer-policies/README.md" /usr/local/share/fased/signer-policies/README.md
4297
4464
  install -m 0644 -o root -g root "$FASED_DIR/config/signer-policies/agent.json.template" /usr/local/share/fased/signer-policies/agent.json.template
4298
4465
  install -m 0644 -o root -g root "$FASED_DIR/config/signer-policies/mining.json.template" /usr/local/share/fased/signer-policies/mining.json.template
@@ -4305,6 +4472,20 @@ install_host_signer_and_updater_services() {
4305
4472
  if [[ ! -f /etc/fased/signerd-webauthn.env ]]; then
4306
4473
  install -m 0644 -o root -g root /dev/null /etc/fased/signerd-webauthn.env
4307
4474
  fi
4475
+ install -m 0644 -o root -g root /dev/null /etc/fased/signerd-sat-runtime.env
4476
+ if [[ -n "${FASED_SAT_PROGRAM_ID:-}" ]]; then
4477
+ cat >/etc/fased/signerd-sat-runtime.env <<EOF
4478
+ FASED_SAT_PROGRAM_ID=${FASED_SAT_PROGRAM_ID}
4479
+ FASED_SAT_BOND_PROGRAM_ID=${FASED_SAT_BOND_PROGRAM_ID}
4480
+ FASED_SAT_MINT_ADDRESS=${FASED_SAT_MINT_ADDRESS}
4481
+ FASED_SAT_MINT_PROGRAM_ID=${FASED_SAT_MINT_PROGRAM_ID}
4482
+ FASED_SAT_RUNTIME_MANIFEST_PATH=${FASED_SAT_RUNTIME_MANIFEST_PATH:-}
4483
+ FASED_SAT_RUNTIME_MANIFEST_SHA256=${FASED_SAT_RUNTIME_MANIFEST_SHA256:-}
4484
+ FASED_SAT_RUNTIME_MANIFEST_SIGNATURE_PATH=${FASED_SAT_RUNTIME_MANIFEST_SIGNATURE_PATH:-}
4485
+ EOF
4486
+ chown root:root /etc/fased/signerd-sat-runtime.env
4487
+ chmod 0644 /etc/fased/signerd-sat-runtime.env
4488
+ fi
4308
4489
  install -d -m 0755 -o root -g root /etc/systemd/system/fased-gateway.service.d
4309
4490
  cat >/etc/systemd/system/fased-gateway.service.d/20-fased-update-gate.conf <<'EOF'
4310
4491
  [Unit]
@@ -4362,7 +4543,6 @@ EOF
4362
4543
  sync -f /usr/local/sbin/fased-signer-enroll
4363
4544
  sync -f /usr/local/sbin/fased-signer-policy
4364
4545
  sync -f /usr/local/sbin/fased-signer-network
4365
- sync -f /usr/local/sbin/fased-signer-wallet-import
4366
4546
  sync -f /usr/local/share/fased/signer-policies/README.md
4367
4547
  sync -f /usr/local/share/fased/signer-policies/agent.json.template
4368
4548
  sync -f /usr/local/share/fased/signer-policies/mining.json.template
@@ -4395,7 +4575,7 @@ Wants=network-online.target
4395
4575
  Type=simple
4396
4576
  User=${signer_user}
4397
4577
  Group=${signer_user}
4398
- SupplementaryGroups=${gateway_group}
4578
+ SupplementaryGroups=${gateway_group} ${operator_group}
4399
4579
  RuntimeDirectory=fased-signerd
4400
4580
  RuntimeDirectoryMode=0755
4401
4581
  StateDirectory=fased-signerd
@@ -4403,7 +4583,8 @@ StateDirectoryMode=0700
4403
4583
  UMask=0077
4404
4584
  Environment=HOME=/var/lib/fased-signerd
4405
4585
  EnvironmentFile=-/etc/fased/signerd-webauthn.env
4406
- ExecStart=/opt/fased/signer/fased-signerd -socket /run/fased-signerd/app.sock -control-socket /run/fased-signerd/control.sock -socket-mode 0660 -socket-group ${gateway_group} -state-db /var/lib/fased-signerd/state.db -master-key /var/lib/fased-signerd/master.key -update-gate /var/lib/fased-signer-update-gate/active -pid-file /run/fased-signerd/fased-signerd.pid -audit-log /var/lib/fased-signerd/audit.jsonl
4586
+ EnvironmentFile=-/etc/fased/signerd-sat-runtime.env
4587
+ ExecStart=/opt/fased/signer/fased-signerd -socket /run/fased-signerd/app.sock -operator-socket /run/fased-signerd/operator.sock -control-socket /run/fased-signerd/control.sock -socket-mode 0660 -socket-group ${gateway_group} -operator-socket-group ${operator_group} -application-uid ${gateway_uid} -operator-uid ${operator_uid} -control-uid ${signer_uid} -state-db /var/lib/fased-signerd/state.db -master-key /var/lib/fased-signerd/master.key -update-gate /var/lib/fased-signer-update-gate/active -pid-file /run/fased-signerd/fased-signerd.pid -audit-log /var/lib/fased-signerd/audit.jsonl
4407
4588
  Restart=always
4408
4589
  RestartSec=3
4409
4590
  NoNewPrivileges=true
@@ -4448,22 +4629,10 @@ EOF
4448
4629
  cat <<'EOF'
4449
4630
  Hosted signer owner handoff
4450
4631
  ---------------------------
4451
- Fresh signer-owned wallets remain deny-all. Enrollment and policy activation are
4452
- separate host-administrator actions; neither the Gateway nor the app account can run them.
4453
-
4454
- After onboarding creates a signer-owned wallet, use a root SSH/provider-console session:
4455
- 1. Copy /usr/local/share/fased/signer-policies/network.json.template to a
4456
- root-owned mode-0600 file, set the exact RPC endpoints, then run:
4457
- /usr/local/sbin/fased-signer-network --wallet-id <native-id> --network-file /root/fased-network.json
4458
- 2. /usr/local/sbin/fased-signer-enroll [authenticator-label]
4459
- 3. cp /usr/local/share/fased/signer-policies/<role>.json.template /root/fased-<role>-policy.json
4460
- 4. Set walletId to the canonical native signer ID (lowercase, separators become
4461
- underscores; do not use a different friendly registry ID), replace every
4462
- REPLACE_WITH_ value, review the exact destinations/caps, then run:
4463
- chmod 0600 /root/fased-<role>-policy.json
4464
- /usr/local/sbin/fased-signer-policy --initial-install --policy-file /root/fased-<role>-policy.json
4465
-
4466
- Copying a template or enrolling a passkey does not enable signing.
4632
+ Fresh wallets receive their versioned Agent, Mining, or Vault baseline from the
4633
+ native signer after the primary RPC is verified. Normal setup does not require a
4634
+ root policy or network helper. Optional approval-device enrollment and advanced
4635
+ custom policy changes remain separate owner operations.
4467
4636
  EOF
4468
4637
  }
4469
4638
 
@@ -4636,7 +4805,6 @@ assert_verified_hosting_root_source() {
4636
4805
  scripts/fased-host-updaterctl.mjs \
4637
4806
  scripts/fased-signer-enroll-hosting.sh \
4638
4807
  scripts/fased-signer-network-hosting.sh \
4639
- scripts/fased-signer-wallet-import-hosting.sh \
4640
4808
  scripts/fased-signer-policy-hosting.sh; do
4641
4809
  local asset_path="$canonical_source/$privileged_asset"
4642
4810
  [[ -f "$asset_path" && ! -L "$asset_path" ]] || {
@@ -4800,6 +4968,11 @@ if [[ -n "${FASED_SAT_PROGRAM_ID:-}" && -n "${FASED_SAT_BOND_PROGRAM_ID:-}" && -
4800
4968
  persist_managed_env_var "FASED_SAT_BOND_PROGRAM_ID" "$FASED_SAT_BOND_PROGRAM_ID"
4801
4969
  persist_managed_env_var "FASED_SAT_MINT_ADDRESS" "$FASED_SAT_MINT_ADDRESS"
4802
4970
  persist_managed_env_var "FASED_SAT_MINT_PROGRAM_ID" "$FASED_SAT_MINT_PROGRAM_ID"
4971
+ if [[ -n "${FASED_SAT_RUNTIME_MANIFEST_PATH:-}" && -n "${FASED_SAT_RUNTIME_MANIFEST_SHA256:-}" && -n "${FASED_SAT_RUNTIME_MANIFEST_SIGNATURE_PATH:-}" ]]; then
4972
+ persist_managed_env_var "FASED_SAT_RUNTIME_MANIFEST_PATH" "$FASED_SAT_RUNTIME_MANIFEST_PATH"
4973
+ persist_managed_env_var "FASED_SAT_RUNTIME_MANIFEST_SHA256" "$FASED_SAT_RUNTIME_MANIFEST_SHA256"
4974
+ persist_managed_env_var "FASED_SAT_RUNTIME_MANIFEST_SIGNATURE_PATH" "$FASED_SAT_RUNTIME_MANIFEST_SIGNATURE_PATH"
4975
+ fi
4803
4976
  else
4804
4977
  :
4805
4978
  fi