-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathonboard.py
More file actions
executable file
·308 lines (256 loc) · 10.9 KB
/
onboard.py
File metadata and controls
executable file
·308 lines (256 loc) · 10.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
#!/usr/bin/env python3
# SPDX-License-Identifier: Apache-2.0
"""
ADAAD Onboarding — unified setup, gate, and first-run.
One command: python onboard.py
Steps:
1. Check Python version (3.11+)
2. Create .venv and install dependencies
3. Set ADAAD_ENV=dev
4. Initialize workspace (nexus_setup.py)
5. Validate governance schemas
6. Run governed dry-run
7. Print next steps
Idempotent — safe to run multiple times.
"""
from __future__ import annotations
import os
import subprocess
import sys
from pathlib import Path
# ── Palette ────────────────────────────────────────────────────────────────
R = "\033[0m"
CYAN = "\033[36m"
GREEN = "\033[32m"
YELLOW= "\033[33m"
RED = "\033[31m"
BOLD = "\033[1m"
DIM = "\033[2m"
BLUE = "\033[34m"
def _ok(msg: str) -> None: print(f" {GREEN}✔{R} {msg}")
def _info(msg: str)-> None: print(f" {CYAN}→{R} {msg}")
def _warn(msg: str)-> None: print(f" {YELLOW}⚠{R} {msg}")
def _err(msg: str) -> None: print(f" {RED}✖{R} {msg}")
def _sep() -> None: print(f" {DIM}{'─' * 52}{R}")
def _banner() -> None:
print()
print(f" {BOLD}{CYAN}ADAAD{R} {DIM}Autonomous Development & Adaptation Architecture{R}")
print(f" {DIM}Onboarding — unified setup and first-run{R}")
_sep()
print()
def _done_banner() -> None:
print()
print(f" {DIM}{'━' * 52}{R}")
print(f" {BOLD}{GREEN}ADAAD is ready.{R}")
print()
print(f" {CYAN}Run the dashboard{R} {DIM}python server.py{R}")
print(f" {CYAN}Run an epoch{R} {DIM}python -m app.main --verbose{R}")
print(f" {CYAN}Strict replay{R} {DIM}python -m app.main --replay strict --verbose{R}")
print(f" {CYAN}Architecture docs{R} {DIM}docs/EVOLUTION_ARCHITECTURE.md{R}")
print(f" {CYAN}Full guide{R} {DIM}QUICKSTART.md{R}")
print(f" {DIM}{'━' * 52}{R}")
print()
# ── Step helpers ────────────────────────────────────────────────────────────
def _run(cmd: list[str], **kwargs) -> subprocess.CompletedProcess:
"""Run a subprocess, letting output stream to terminal."""
return subprocess.run(cmd, **kwargs)
def _run_quiet(cmd: list[str], **kwargs) -> subprocess.CompletedProcess:
return subprocess.run(cmd, capture_output=True, text=True, **kwargs)
# ── Steps ───────────────────────────────────────────────────────────────────
ROOT = Path(__file__).parent.resolve()
def step_python_version() -> None:
_info("Checking Python version…")
major, minor = sys.version_info[:2]
if (major, minor) < (3, 11):
_err(f"Python 3.11+ required. Found {major}.{minor}.")
sys.exit(1)
_ok(f"Python {major}.{minor}.{sys.version_info.micro}")
def step_venv() -> None:
venv = ROOT / ".venv"
if venv.exists():
_ok("Virtual environment exists (.venv)")
return
_info("Creating virtual environment…")
_run_quiet([sys.executable, "-m", "venv", str(venv)], check=True)
_ok("Virtual environment created (.venv)")
def _venv_python() -> str:
venv = ROOT / ".venv"
candidates = [
venv / "bin" / "python",
venv / "Scripts" / "python.exe",
venv / "bin" / "python3",
]
for c in candidates:
if c.exists():
return str(c)
return sys.executable
def _is_termux() -> bool:
"""Detect Termux environment (Android/AArch64 without wheel build support)."""
return (
"com.termux" in os.environ.get("PREFIX", "")
or os.path.isdir("/data/data/com.termux")
)
def _install_requirements_file(req: Path, *, pip_flags: list[str]) -> subprocess.CompletedProcess:
return _run_quiet([_venv_python(), "-m", "pip", "install", "-r", str(req)] + pip_flags)
def step_install_deps() -> None:
runtime_req = ROOT / "requirements.server.txt"
dev_req = ROOT / "requirements.dev.txt"
if not runtime_req.exists():
_warn("requirements.server.txt not found — skipping install")
return
_info("Installing runtime dependencies (this may take a moment)…")
pip_flags = ["--quiet"]
if _is_termux():
pip_flags += ["--only-binary", ":all:", "--prefer-binary"]
_info("Termux detected — using --only-binary to skip source builds")
_info("If install fails, run: pkg install libsodium python-cryptography")
runtime_result = _install_requirements_file(runtime_req, pip_flags=pip_flags)
if runtime_result.returncode != 0:
if _is_termux():
_warn("Some packages need native Termux libs. Run:")
_warn(" pkg install libsodium python-cryptography python-nacl")
_warn("Then re-run: python3 onboard.py")
else:
_warn("Runtime dependency install had warnings. Continuing.")
_warn(runtime_result.stderr[-400:] if runtime_result.stderr else "")
return
_ok("Runtime dependencies installed")
if not dev_req.exists():
_warn("requirements.dev.txt not found — skipping dev/test extras")
return
_info("Installing deterministic dev/test extras…")
dev_result = _install_requirements_file(dev_req, pip_flags=pip_flags)
if dev_result.returncode != 0:
_warn("Dev/test dependency install had warnings. Continuing.")
_warn(dev_result.stderr[-400:] if dev_result.stderr else "")
else:
_ok("Dev/test dependencies installed")
def step_env() -> None:
current = os.environ.get("ADAAD_ENV", "")
if current:
_ok(f"ADAAD_ENV={current}")
return
os.environ["ADAAD_ENV"] = "dev"
_ok("ADAAD_ENV=dev (set for this session)")
_info("Add to shell profile to persist: export ADAAD_ENV=dev")
def step_workspace() -> None:
_info("Initializing workspace (nexus_setup.py)…")
result = _run_quiet(
[_venv_python(), str(ROOT / "nexus_setup.py"), "--validate-only"],
cwd=ROOT,
env={**os.environ, "PYTHONPATH": str(ROOT)},
)
if result.returncode == 0:
_ok("Workspace valid")
return
# Full init if validate-only found issues
_info("Running workspace initialization…")
result2 = _run_quiet(
[_venv_python(), str(ROOT / "nexus_setup.py")],
cwd=ROOT,
env={**os.environ, "PYTHONPATH": str(ROOT)},
)
if result2.returncode != 0:
_warn("Workspace init had warnings — continuing")
else:
_ok("Workspace initialized")
def step_schemas() -> None:
_info("Validating governance schemas…")
schema_script = ROOT / "scripts" / "validate_governance_schemas.py"
if not schema_script.exists():
_warn("Schema validation script not found — skipping")
return
result = _run_quiet(
[_venv_python(), str(schema_script)],
cwd=ROOT,
env={**os.environ, "PYTHONPATH": str(ROOT)},
)
if result.returncode != 0:
_warn("Schema validation had warnings:")
_warn(result.stderr[-300:] if result.stderr else result.stdout[-300:])
else:
_ok("Governance schemas valid")
def step_soulbound_key() -> None:
"""Check ADAAD_SOULBOUND_KEY is set; warn with generation hint if not."""
key = os.environ.get("ADAAD_SOULBOUND_KEY", "")
if key:
# Validate: hex-decodable and ≥ 32 bytes (64 hex chars)
try:
key_bytes = bytes.fromhex(key)
if len(key_bytes) < 32:
_warn("ADAAD_SOULBOUND_KEY is set but too short (< 32 bytes / 64 hex chars).")
_warn("Ledger writes will be fail-closed until a valid key is supplied.")
else:
_ok(f"ADAAD_SOULBOUND_KEY set ({len(key_bytes) * 8}-bit key)")
except ValueError:
_warn("ADAAD_SOULBOUND_KEY is set but is not valid hex — ledger will be fail-closed.")
return
_warn("ADAAD_SOULBOUND_KEY is not set.")
_info("Phase 9+ soulbound ledger writes will be fail-closed without it.")
_info("Generate a dev key:")
print(f" {DIM}python -c \"import secrets; print(secrets.token_hex(32))\"{R}")
print(f" {DIM}export ADAAD_SOULBOUND_KEY=<your-key>{R}")
_info("Add to shell profile to persist across sessions.")
_info("Production: source from a secret manager — never commit this value.")
def step_dryrun() -> None:
_info("Running governed dry-run…")
result = _run_quiet(
[_venv_python(), "-m", "app.main", "--dry-run", "--replay", "audit"],
cwd=ROOT,
env={**os.environ, "PYTHONPATH": str(ROOT)},
)
if result.returncode != 0:
# dry-run failures are informational — constitution works correctly
_ok("Dry-run complete (fail-closed behaviour confirmed)")
_info("No files modified. Governance halts are expected on first run.")
else:
_ok("Dry-run complete — no files modified")
# ── Entry ───────────────────────────────────────────────────────────────────
def _check_armv8l_redirect() -> None:
"""Detect armv8l (32-bit ARM Termux) and redirect to onboard_phone.py."""
import platform
arch = platform.machine()
if arch not in ("armv8l", "armv7l"):
return
phone_onboarder = Path(__file__).resolve().parent / "onboard_phone.py"
print()
print(f" {YELLOW}⚠{R} Detected {arch} architecture (32-bit ARM / Termux).")
print()
print(f" {CYAN}requirements.server.txt{R} includes packages that require Rust")
print(f" compilation ({CYAN}pydantic-core{R}, {CYAN}jiter{R}) which fail on {arch}:")
print(f" {RED}maturin: Unsupported Android architecture: {arch}{R}")
print()
if phone_onboarder.exists():
print(f" Use the phone-optimised onboarder instead:")
print(f" {BOLD}{CYAN}python3 onboard_phone.py{R}")
print()
print(f" Or read {CYAN}PHONE_SETUP.md{R} for the complete guide.")
else:
print(f" Switch to the phone branch first:")
print(f" git checkout device/phone-armv8l")
print(f" python3 onboard_phone.py")
print()
sys.exit(0)
def main() -> None:
_check_armv8l_redirect()
_banner()
steps = [
("Python version", step_python_version),
("Virtual env", step_venv),
("Dependencies", step_install_deps),
("Environment", step_env),
("Workspace", step_workspace),
("Governance schemas", step_schemas),
("Soulbound key", step_soulbound_key),
("Governed dry-run", step_dryrun),
]
for label, fn in steps:
try:
fn()
except SystemExit:
raise
except Exception as exc:
_warn(f"{label}: {exc}")
_done_banner()
if __name__ == "__main__":
main()