direxio-deployer 0.1.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 (77) hide show
  1. package/AGENTS.md +92 -0
  2. package/LICENSE +21 -0
  3. package/README.md +221 -0
  4. package/README_zh.md +218 -0
  5. package/SKILL.md +722 -0
  6. package/agents/README.md +25 -0
  7. package/agents/openai.yaml +12 -0
  8. package/bin/direxio-deployer.mjs +375 -0
  9. package/package.json +28 -0
  10. package/references/agent-targets.md +128 -0
  11. package/references/architecture.md +44 -0
  12. package/references/bug-history.md +78 -0
  13. package/references/deployment-lessons.md +218 -0
  14. package/references/deployment-optimization-audit.md +317 -0
  15. package/references/deployment-workflow.md +341 -0
  16. package/references/iam-policy.json +52 -0
  17. package/references/runtime-wiring.md +209 -0
  18. package/references/state-machine.md +46 -0
  19. package/references/token-refresh.md +81 -0
  20. package/references/tooling.md +106 -0
  21. package/references/troubleshooting.md +26 -0
  22. package/references/user-journey.md +75 -0
  23. package/references/verification-recovery.md +84 -0
  24. package/references/voip-turn-runbook.md +154 -0
  25. package/references/windows-deployment-notes.md +119 -0
  26. package/scripts/aws-credentials.sh +195 -0
  27. package/scripts/cloud-init/Caddyfile +48 -0
  28. package/scripts/cloud-init/docker-compose.yml +125 -0
  29. package/scripts/cloud-init/init-tokens.sh +238 -0
  30. package/scripts/cloud-init/user-data.yaml +40 -0
  31. package/scripts/destroy.ps1 +77 -0
  32. package/scripts/destroy.sh +589 -0
  33. package/scripts/lib/aws.sh +73 -0
  34. package/scripts/lib/domain.sh +175 -0
  35. package/scripts/lib/operation_report.sh +240 -0
  36. package/scripts/lib/ops.sh +230 -0
  37. package/scripts/lib/paths.sh +35 -0
  38. package/scripts/lib/state.sh +137 -0
  39. package/scripts/mcp-tools-list.mjs +95 -0
  40. package/scripts/orchestrate.ps1 +112 -0
  41. package/scripts/orchestrate.sh +1126 -0
  42. package/scripts/phases/s0_prereq_aws.sh +39 -0
  43. package/scripts/phases/s1_preflight.sh +72 -0
  44. package/scripts/phases/s2_domain.sh +103 -0
  45. package/scripts/phases/s3_provision.sh +421 -0
  46. package/scripts/phases/s4_bootstrap_stack.sh +38 -0
  47. package/scripts/phases/s5_init_tokens.sh +118 -0
  48. package/scripts/phases/s6_wire_local.sh +1435 -0
  49. package/scripts/phases/s7_verify_e2e.sh +136 -0
  50. package/scripts/pricing-estimate.sh +256 -0
  51. package/scripts/render/render-userdata.sh +86 -0
  52. package/scripts/reset-app-data.sh +40 -0
  53. package/scripts/update.sh +30 -0
  54. package/tests/aws_credentials_test.sh +139 -0
  55. package/tests/connect_daemon_runtime_check_test.sh +120 -0
  56. package/tests/default_paths_test.sh +58 -0
  57. package/tests/destroy_local_bridge_test.sh +154 -0
  58. package/tests/destroy_root_identity_test.sh +91 -0
  59. package/tests/destroy_route53_zone_test.sh +80 -0
  60. package/tests/domain_authoritative_dns_test.sh +49 -0
  61. package/tests/mcp_doctor_runtime_check_test.sh +86 -0
  62. package/tests/mcp_smoke_runtime_check_test.sh +121 -0
  63. package/tests/mcp_tools_runtime_check_test.sh +123 -0
  64. package/tests/npm_skill_distribution_test.sh +95 -0
  65. package/tests/operation_report_test.sh +258 -0
  66. package/tests/orchestrate_status_recovery_test.sh +91 -0
  67. package/tests/phase_timeout_test.sh +88 -0
  68. package/tests/pricing_estimate_test.sh +159 -0
  69. package/tests/render_userdata_remote_nodes_test.sh +40 -0
  70. package/tests/root_volume_tracking_test.sh +41 -0
  71. package/tests/route53_overwrite_guard_test.sh +86 -0
  72. package/tests/route53_zone_auto_create_test.sh +66 -0
  73. package/tests/runtime_summary_check_test.sh +203 -0
  74. package/tests/s6_wire_local_test.sh +405 -0
  75. package/tests/skill_structure_test.sh +298 -0
  76. package/tests/update_reset_ops_test.sh +230 -0
  77. package/tests/user_confirmation_gates_test.sh +152 -0
@@ -0,0 +1,118 @@
1
+ #!/usr/bin/env bash
2
+ # S5 INIT_TOKENS - fetch AS-written bootstrap credentials from the instance.
3
+ # Also verify owner.json so the client does not report Portal as undeployed.
4
+
5
+ run_phase() {
6
+ phase_set S5_INIT_TOKENS in_progress "fetching tokens"
7
+ local domain pubip keyfile
8
+ domain=$(state_get domain)
9
+ pubip=$(res_get public_ip)
10
+ keyfile=$(res_get key_file)
11
+ local out="$P2P_WORKDIR/outputs.json" raw
12
+ raw=$(mktemp)
13
+ trap 'rm -f "${raw:-}"; trap - RETURN' RETURN
14
+
15
+ log "Fetching /opt/p2p/bootstrap.json ..."
16
+ if ! poll_until "read bootstrap.json" "${TOKEN_POLL_INTERVAL:-10}" "${TOKEN_POLL_MAX:-12}" \
17
+ _read_remote_bootstrap "$keyfile" "$pubip" "$raw"; then
18
+ phase_set S5_INIT_TOKENS failed "failed to fetch bootstrap.json"
19
+ warn "Could not read /opt/p2p/bootstrap.json. Check whether message-server wrote credentials:"
20
+ warn " ssh -i $keyfile ubuntu@$pubip 'sudo cat /opt/p2p/bootstrap.json 2>/dev/null; cd /opt/p2p; sudo docker compose logs message-server | tail -40'"
21
+ return 1
22
+ fi
23
+ if ! _normalize_bootstrap_output "$domain" "$raw" "$out"; then
24
+ phase_set S5_INIT_TOKENS failed "invalid bootstrap.json"
25
+ fail "bootstrap.json could not be normalized."
26
+ fi
27
+
28
+ # Verify owner.json; missing file makes the client report Portal as undeployed.
29
+ if _healthz_ok_ownerjson "$domain"; then
30
+ log "owner.json 200 OK (Portal discovery healthy)"
31
+ else
32
+ warn "/.well-known/portal/owner.json did not return 200. The client may report Portal as undeployed."
33
+ warn " Check Caddy file_server and /opt/p2p/wellknown/owner.json generation."
34
+ fi
35
+
36
+ local password token access_token asurl agent_room_id
37
+ if ! IFS=$'\t' read -r password token access_token < <(_extract_output_tokens "$out"); then
38
+ phase_set S5_INIT_TOKENS failed "bootstrap.json missing password/access/agent credentials"
39
+ fail "bootstrap.json must contain password as an eight-digit initialization-code string plus access_token and agent_token."
40
+ fi
41
+ asurl=$(jq -r --arg domain "$domain" '.as_url // ("https://" + $domain)' "$out")
42
+ agent_room_id=$(jq -r '.agent_room_id // empty' "$out")
43
+ if [ -z "$agent_room_id" ] || [[ "$agent_room_id" == \!agent:* ]]; then
44
+ phase_set S5_INIT_TOKENS failed "bootstrap.json missing real agent_room_id"
45
+ fail "bootstrap.json must contain a real Matrix agent_room_id; legacy !agent:<domain> ids are not supported."
46
+ fi
47
+
48
+ # Store tokens in state for S6. state.json is local-only and chmod 0600.
49
+ state_set as_url "$asurl"
50
+ state_set password "$password"
51
+ state_set agent_token "$token"
52
+ state_set access_token "$access_token"
53
+ state_set agent_room_id "$agent_room_id"
54
+
55
+ phase_set S5_INIT_TOKENS done "got password (len=${#password}) as_url=$asurl agent_room_id=$agent_room_id"
56
+ ok "Tokens fetched from bootstrap.json."
57
+ return 0
58
+ }
59
+
60
+ _extract_output_tokens() {
61
+ local out=$1 password token access_token
62
+ password=$(jq -r 'if (.password | type) == "string" then .password else empty end' "$out")
63
+ token=$(jq -r '.agent_token // empty' "$out")
64
+ access_token=$(jq -r '.access_token // empty' "$out")
65
+ [ -n "$password" ] && [ -n "$token" ] && [ -n "$access_token" ] || return 1
66
+ printf '%s' "$password" | grep -Eq '^[0-9]{8}$' || return 1
67
+ printf '%s\t%s\t%s\n' "$password" "$token" "$access_token"
68
+ }
69
+
70
+ _read_remote_bootstrap() {
71
+ local keyfile=$1 pubip=$2 out=$3
72
+ local ssh_args cmd timeout_seconds
73
+ ssh_args=(
74
+ -i "$keyfile"
75
+ -o StrictHostKeyChecking=accept-new
76
+ -o ConnectTimeout="${SSH_CONNECT_TIMEOUT:-10}"
77
+ -o BatchMode=yes
78
+ -o ServerAliveInterval="${SSH_SERVER_ALIVE_INTERVAL:-5}"
79
+ -o ServerAliveCountMax="${SSH_SERVER_ALIVE_COUNT_MAX:-2}"
80
+ )
81
+ cmd=(ssh "${ssh_args[@]}" ubuntu@"$pubip" "sudo test -s /opt/p2p/bootstrap.json && sudo cat /opt/p2p/bootstrap.json")
82
+ timeout_seconds=${SSH_COMMAND_TIMEOUT:-30}
83
+ if command -v timeout >/dev/null 2>&1; then
84
+ timeout "$timeout_seconds" "${cmd[@]}" > "$out" 2>/dev/null
85
+ else
86
+ "${cmd[@]}" > "$out" 2>/dev/null
87
+ fi
88
+ }
89
+
90
+ _normalize_bootstrap_output() {
91
+ local domain=$1 src=$2 out=$3
92
+ local tmp
93
+ tmp=$(mktemp)
94
+ if ! jq --arg domain "$domain" --arg asurl "https://$domain" '
95
+ . + {
96
+ domain: (.domain // $domain),
97
+ as_url: (.as_url // $asurl),
98
+ p2p_url: (.p2p_url // $asurl),
99
+ user_id: (.user_id // .owner_user_id // ""),
100
+ bot_mxid: (.bot_mxid // .owner_user_id // .user_id // ("@owner:" + $domain)),
101
+ access_token: (.access_token // ""),
102
+ agent_token: (.agent_token // ""),
103
+ agent_room_id: (.agent_room_id // "")
104
+ }
105
+ ' "$src" > "$tmp"; then
106
+ rm -f "$tmp"
107
+ return 1
108
+ fi
109
+ mv "$tmp" "$out"
110
+ chmod 600 "$out" 2>/dev/null || true
111
+ }
112
+
113
+ _healthz_ok_ownerjson() {
114
+ local domain=$1 pubip args=()
115
+ pubip=$(res_get public_ip)
116
+ [ -n "$pubip" ] && args=(--resolve "$domain:443:$pubip")
117
+ [ "$(curl -sk "${args[@]}" -o /dev/null -w '%{http_code}' "https://$domain/.well-known/portal/owner.json" 2>/dev/null)" = "200" ]
118
+ }