Install saga: 17 sequential boot-blocking failures, misplaced and missing binaries, and a restart loop the runtime inflicts on itself
Summary
Getting a working install on this machine took 17 distinct boot-blocking failures, resolved one at a time, each only visible after fixing the one before it. Not one of them printed a remedy. Several are self-inflicted by the running product: it writes files and directories that its own next boot refuses to accept.
I am still not fully up. The current blocker destroys audit records if I clear it the way I cleared the six before it, so I stopped.
The headline problems:
- 2026.9.2 ships a
release-manifest.jsonlayout contract the installer does not honour. The manifest declares six executables underreleases/2026.9.2/runtime/bin/. The installer writes them to~/.aos/runtime/bin/and~/.aos/bin/instead. Everyaoscommand that needs the runtime fails withbundled runtime executable not found. - One declared executable is never installed at all —
astrid-storage-provider-fskitexists in no location on disk. - The runtime cannot restart after it has run. It recreates seven legacy paths during normal operation that the boot-time migration barrier rejects as "reappeared after cut-over". Boot → run → write → next boot fails. Clearing one just surfaces the next.
- The runtime creates group-readable files and directories that its own owner-only check rejects. I fixed 23 of them; 10 fresh ones appeared within three minutes of the daemon running.
aos startbinds the daemon to the current working directory, soaos initfrom anywhere else fails with "running daemon belongs to another project".
Nothing here was corrupt. I verified every downloaded binary against the signed manifest — all sha256 match. The bits are fine; the install, layout and migration logic are not.
Environment
| OS | macOS 15.6 (Darwin 24.6.0), arm64 |
| AOS before | 2026.1.3 (20 Jul), runtime 0.10.4 |
| AOS after | 2026.9.2 (13 Sep), runtime 2026.9.2 |
| Legacy stack | ~/.astrid, astrid 2026.9.2, first created 2026-05-24 |
| Plugins | astrid@astrid-oracles 0.1.4 (legacy), unicity-aos@unicity-aos-oracles 0.1.9 |
| Date of this session | 2026-09-15 |
Part 1 — The legacy ~/.astrid stack: 10 failures, ending in an unrecoverable one
This is where I started. The user ran astrid start and got Daemon exited prematurely (exit status: 1). The boot log gave one error. Fixing it gave the next. Ten in a row.
| # | Error | What it actually was | Fix |
|---|---|---|---|
| 1 | unexpected entry in released legacy SurrealKV source: .DS_Store |
Finder had written .DS_Store into var/state.db. The retirement allow-list rejects any unrecognised entry. |
rm the file (12 of them existed under ~/.astrid) |
| 2 | activate direct representation catalogue: … active direct profile names a different frozen specification |
Store frozen under PRE_FLEET_OWNER; the call site accepts exactly one prior spec (PRE_DENSE_RADIX) while the format list beside it has 13 |
move var/principal-store/representations/ aside so it rebuilds |
| 3 | private directory is not owner-only: ~/.astrid/cow |
dir was 0755, check demands exactly 0700 |
chmod 700 |
| 4 | private file is not owner-only: ~/.astrid/cow/.DS_Store |
more Finder junk, now failing the file mode check | rm |
| 5 | private file is not owner-only: …/log/a2a-capsule/2026-08-29.log |
421 files/dirs created 0644/0755 by earlier releases |
chmod -R go-rwx ~/.astrid |
| 6 | decode Distro.lock: unknown field 'resolved_ref', expected … 'resolved-ref' |
the CLI writes snake_case, the kernel reads kebab-case with deny_unknown_fields |
rewrite the key in 2 lockfiles |
| 7 | legacy capsule migration failed: verify legacy capsule authority astrid-capsule-agents |
8 of 19 capsules had authority receipts one version behind their manifest | delete the 10 stale receipts |
| 8 | legacy log tree exceeds migration byte limit |
858 MB of logs from astrid-capsule-srouter, a capsule no longer installed. Limit is 64 MiB. Nothing rotates or reclaims them. |
move the orphan log dir away |
| 9 | legacy env/secret sources remain for claude-code (uid 43f45f18…); migration API did not retire every scope |
capsules owned by default, env/secrets owned by claude-code; the importer iterates capsules, then asserts over the filesystem |
move orphaned scopes away |
| 10 | legacy import receipt conflicts with durable state |
terminal. The import writes a receipt derived from item counts, then deletes the sources. Next boot recomputes env=0 secrets=0, disagrees with the stored receipt, and hard-fails forever. |
none — unrecoverable |
Failure 10 cannot be undone from outside the process. A boot that fails after committing one scope can never boot again.
These are filed individually against the runtime: astrid-runtime/astrid#1941 · #1942 · #1943 · #1944 · #1945 · #1946 · #1947 · #1948 · #1949
Ten failures, and not one message said "this product has been superseded by Unicity AOS, which is already installed on your machine at ~/.aos." It was.
Part 2 — Nothing told me the product had been renamed
With ~/.astrid wiped, I followed the legacy plugin's own repair advice (bin/astrid-doctor:209, :357):
astrid init --distro https://raw.githubusercontent.com/unicity-astrid/oracles/main/distros/claude.toml -y
✗ error: failed to fetch Distro.toml … (HTTP 404 Not Found)Three separate things were stale in that one URL:
- org renamed —
unicity-astrid→unicity-aos - directory renamed —
distros/→packs/ - format changed —
[distro]→[pack],astrid-capsule-*→aos-*, andaos-version = ">=2026.9.1"
Correcting the first two still fails, because the astrid CLI cannot parse the third:
✗ error: failed to parse Distro.toml: TOML parse error at line 1, column 1
|
1 | schema-version = 1
| ^
missing field `distro`Only then, poking around the filesystem, did I find ~/.aos — Unicity AOS 2026.1.3, installed 6 Sep, running the whole time. Filed as unicity-aos/oracles#66.
Worth stating plainly: the product had moved on, an install of the new product was already running on the same machine, and the user spent this entire session being told to run astrid start.
Part 3 — aos update succeeded, then nothing could use the result
The update itself was clean:
Downloading the pinned Sigstore verifier...
Resolving the signed stable channel...
Verified OK
Verified OK
Downloading Unicity AOS 2026.9.2 for aarch64-apple-darwin...
Verified OK
Verified OK
i Installed Unicity AOS 2026.9.2.
Run: aos initThen:
$ aos init
aos: failed to prepare the bundled runtime for CE init: bundled runtime executable not found
at /Users/jamie/.aos/releases/2026.9.2/runtime/bin/astrid
$ aos start
aos: failed to start bundled runtime: bundled runtime executable not found
at /Users/jamie/.aos/releases/2026.9.2/runtime/bin/astrid3a. The manifest declares a layout the installer does not produce
releases/2026.9.2/release-manifest.json:
"executables": [
"bin/aos",
"runtime/bin/astrid",
"runtime/bin/astrid-daemon",
"runtime/bin/astrid-build",
"runtime/bin/astrid-emit",
"runtime/bin/astrid-storage-provider-fskit"
],
"layout": {
"release_directory": "releases/2026.9.2",
"runtime_executables": "runtime/bin",
"capsule_assets": "capsules"
}What the installer actually produced — no runtime/ directory at all:
/Users/jamie/.aos/releases/2026.9.2
├── Distro.toml
├── capsule-assets.txt
├── capsules/ (22 .capsule files)
└── release-manifest.jsonThe executables went to their live locations instead:
~/.aos/bin/aos
~/.aos/runtime/bin/astrid
~/.aos/runtime/bin/astrid-daemon
~/.aos/runtime/bin/astrid-build
~/.aos/runtime/bin/astrid-emitThis looks like a schema-2 regression. The previous release's manifest declares neither key:
2026.1.3 manifest → executables: None layout: None schema_version: 2
2026.9.2 manifest → executables: [6] layout: {...} schema_version: 2So 2026.9.2 added a layout contract that the CLI now reads, while the installer kept the old placement — and the two schema versions are both 2, so nothing detects the mismatch.
3b. Misplaced, but not corrupt
I verified every binary against the signed manifest. All three match exactly:
manifest runtime/bin/astrid f9cba77cd002daf0ddffb754dc17b06a45f7dca26008d2b88feee9745f95ac0d
on disk f9cba77cd002daf0ddffb754dc17b06a45f7dca26008d2b88feee9745f95ac0d ✓
manifest runtime/bin/astrid-daemon e6cfbd61677c06569b026d0275f88461972bc4eed2d660f6570e500c5481355e
on disk e6cfbd61677c06569b026d0275f88461972bc4eed2d660f6570e500c5481355e ✓
manifest bin/aos a9c6b0322c4671dfffb5715063bda2e7897c55a993e2e814cb41cb3e71b1404e
on disk a9c6b0322c4671dfffb5715063bda2e7897c55a993e2e814cb41cb3e71b1404e ✓The download and signature path is sound. Only the placement is wrong.
3c. One declared executable was never installed
astrid-storage-provider-fskit is in the manifest's executables list and in release_files, and exists nowhere on disk:
$ find ~/.aos -name '*fskit*'
(no output)
$ ls ~/.aos/runtime/bin/ | grep -v wasm
astrid
astrid-build
astrid-daemon
astrid-emitFive of six installed, one silently dropped, and aos update reported success. Whatever verifies the install against release_files either does not run or does not fail.
Workaround I applied: created releases/2026.9.2/runtime/bin/ and hardlinked the four present binaries into it. aos start then resolved. The fskit provider remains missing.
Part 4 — The doom loop: the runtime cannot restart after it has run
With the path fixed, the daemon started, reached ready in about two minutes, and reported healthy:
Unicity AOS
State: running
PID: 65241
Uptime: 132s
Runtime version: 2026.9.2
Connected clients: 1
Loaded capsules: 4Then I restarted it. It has not come back since.
The mechanism: during normal operation the runtime creates paths under home/<principal>/.local/ and home/<principal>/.config/. On the next boot the migration barrier treats the existence of those same paths as proof that a retired legacy source has come back, and refuses to boot.
Every distinct failure, in the order the barrier surfaced them — one per boot attempt:
Error: Failed to boot Kernel: legacy kv source reappeared after cut-over:
/Users/jamie/.aos/runtime/home/default/.local/kv
Error: Failed to boot Kernel: legacy tokens source reappeared after cut-over:
/Users/jamie/.aos/runtime/home/default/.local/tokens
Error: Failed to boot Kernel: legacy env source reappeared after cut-over:
/Users/jamie/.aos/runtime/home/default/.config/env
Error: Failed to boot Kernel: legacy principal default
(uid 943e7afd9ce1ae59b7e7e0cc9c934b01537e94c2cda2ec3e15b784f372ef1d70)
retains audit source after its importer:
/Users/jamie/.aos/runtime/home/default/.local/auditThe first six were empty directories the daemon itself had just created, timestamped minutes after I had deleted them:
$ ls -A ~/.aos/runtime/home/default/.local/kv | wc -l
0
$ stat -f '%Sm' ~/.aos/runtime/home/default/.local/kv
Sep 15 10:14 # created by the daemon; I deleted it at 10:13I cleared them in turn — kv, tokens, capsules, log, tmp, then .config/env. Each clearance produced exactly one more boot, which named the next path. Six rounds.
The seventh is where I stopped. home/default/.local/audit is not empty — it holds audit records the daemon wrote during its 132 seconds of uptime. Deleting it would destroy audit data and it would be recreated on the next run, so the loop does not terminate. That is not a workaround, it is a shredder.
This is the core defect. A freshly updated runtime that boots successfully, runs for two minutes, and is then restarted cannot start again. There is no operator action that breaks the cycle, because the thing being rejected is the product's own working state.
4a. A stale capsule name in the same log
Interleaved through all of the above, referencing the pre-rename capsule id and a 404 install command:
Error: CLI proxy capsule (astrid-capsule-cli) not found.
Install it with: astrid capsule install @unicity-astrid/capsule-cliThe installed distro provides aos-cli, not astrid-capsule-cli, and @unicity-astrid/* no longer resolves.
Part 5 — The permissions loop, self-inflicted
The runtime enforces exactly 0700 on directories and 0600 on files, and creates neither.
First boot after the update:
Error: Failed to boot Kernel: private file is not owner-only:
/Users/jamie/.aos/runtime/home/claude-code/.local/log/aos-cli/2026-09-15.log23 entries under ~/.aos/runtime failed the check, including the newly installed binaries:
~/.aos/runtime/bin/astrid
~/.aos/runtime/bin/astrid-daemon
~/.aos/runtime/bin/astrid-build
~/.aos/runtime/bin/astrid-emit
~/.aos/runtime/var/update-check.json
~/.aos/runtime/log/astrid.2026-09-12.log
...I ran chmod -R go-rwx ~/.aos/runtime — zero remaining. The daemon then ran for about three minutes, and 10 fresh group-readable directories appeared, created by the product itself:
~/.aos/runtime/var/state.db/wal
~/.aos/runtime/var/state.db/sstables
~/.aos/runtime/home/default/.config/env
~/.aos/runtime/home/default/.local/kv
~/.aos/runtime/home/default/.local/capsules
~/.aos/runtime/home/default/.local/log
~/.aos/runtime/home/default/.local/tmp
~/.aos/runtime/home/default/.local/tokens
~/.aos/runtime/home/default/.local/audit/wal
~/.aos/runtime/home/default/.local/audit/sstablesThe writer and the checker disagree, inside one version. I had to chmod -R go-rwx before every start attempt. This is astrid-runtime/astrid#1946, which I filed against the legacy stack before discovering it is equally live on 2026.9.2 — and self-inflicted here, not inherited from an old install.
Note also that the file rule is exactly 0600, so an executable at 0700 fails it. The runtime binaries sit in a tree subject to these checks.
Part 6 — aos start binds the daemon to your shell's working directory
aos start launches:
astrid-daemon --workspace /Users/jamie/Code/astrid— the cwd, not ~/.aos/runtime. Run aos init from anywhere else and:
✗ error: init could not ensure the runtime daemon: running daemon belongs to
another project or workspace layout; run `astrid restart` from this projectSo aos start and aos init must be run from the same directory, and nothing says so. The remedy it offers (astrid restart) names a binary that is not on PATH after a normal AOS install.
Everything I had to do by hand
For a supported aos update on a supported platform:
chmod -R go-rwx ~/.aos/runtime— before every single start attemptmkdir -p ~/.aos/releases/2026.9.2/runtime/binand hardlink four binaries into it- Delete stale
run/system.sock,run/system.pid,run/system.readyfrom a dead daemon - Delete six empty legacy directories the daemon had just created, one per boot attempt
- Discover the cwd binding by reading
psoutput - Accept that
astrid-storage-provider-fskitis simply absent - Stop at the seventh legacy path, because clearing it destroys audit records
None of these are documented. All of them required reading source, ps, find, stat and shasum to work out.
What I would expect instead
Install and layout
- Honour the manifest's own
layoutcontract, or drop the keys. Today the CLI reads them and the installer ignores them. - Verify the install against
release_filesand fail loudly. A missing executable must not reportInstalled Unicity AOS 2026.9.2. - Bump
schema_versionwhen the layout contract changes. 2026.1.3 and 2026.9.2 both claim2and mean different things.
Migration barrier
- Distinguish "a legacy source was never retired" from "the running product recreated this path". An empty directory the daemon made 40 seconds ago is not evidence of an un-migrated install.
- Never refuse to boot on a path the product itself creates during normal operation.
- Report all offending paths in one pass. Seven boots to learn seven paths is not a diagnostic experience.
Permissions
- Create files
0600and directories0700in the first place. - Repair modes during upgrade rather than aborting on the first offender.
- Report every non-conforming path at once, with the remedy.
Messages
- Every boot refusal should name the remedy. Not one of these 17 did.
- Do not reference pre-rename capsule ids or dead
@unicity-astrid/*install commands.
Cross-generation
- When a retired
~/.astridinstall is present and~/.aosexists, say so.aos migratealready exists for exactly this handover, and I found it only after the entire Part 1 investigation.
Current state
aos --version→Unicity AOS 2026.9.2~/.aos/runtime/bin/astrid --version→astrid 2026.9.2distributions/unicity-ce/Distro.toml→ 22 capsules, correctly repointed atreleases/2026.9.2- Daemon: not running. Blocked on
retains audit source after its importer. astrid-storage-provider-fskit: still missing.
Source: unicity-aos/aos-ce