#177·aos-ce

Install saga: 17 sequential boot-blocking failures, misplaced and missing binaries, and a restart loop the runtime inflicts on itself

Author: jvsteinerCreated Sep 15, 2026Updated Sep 15, 2026
Labelsbugneeds-design

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.json layout contract the installer does not honour. The manifest declares six executables under releases/2026.9.2/runtime/bin/. The installer writes them to ~/.aos/runtime/bin/ and ~/.aos/bin/ instead. Every aos command that needs the runtime fails with bundled runtime executable not found.
  • One declared executable is never installed at allastrid-storage-provider-fskit exists 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 start binds the daemon to the current working directory, so aos init from 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:

  1. org renamedunicity-astridunicity-aos
  2. directory renameddistros/packs/
  3. format changed[distro][pack], astrid-capsule-*aos-*, and aos-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 init

Then:

$ 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/astrid

3a. The manifest declares a layout the installer does not produce

releases/2026.9.2/release-manifest.json:

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.json

The 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-emit

This 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: 2

So 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-emit

Five 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: 4

Then 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/audit

The 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:13

I 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-cli

The 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.log

23 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/sstables

The 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 project

So 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:

  1. chmod -R go-rwx ~/.aos/runtime — before every single start attempt
  2. mkdir -p ~/.aos/releases/2026.9.2/runtime/bin and hardlink four binaries into it
  3. Delete stale run/system.sock, run/system.pid, run/system.ready from a dead daemon
  4. Delete six empty legacy directories the daemon had just created, one per boot attempt
  5. Discover the cwd binding by reading ps output
  6. Accept that astrid-storage-provider-fskit is simply absent
  7. 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 layout contract, or drop the keys. Today the CLI reads them and the installer ignores them.
  • Verify the install against release_files and fail loudly. A missing executable must not report Installed Unicity AOS 2026.9.2.
  • Bump schema_version when the layout contract changes. 2026.1.3 and 2026.9.2 both claim 2 and 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 0600 and directories 0700 in 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 ~/.astrid install is present and ~/.aos exists, say so. aos migrate already exists for exactly this handover, and I found it only after the entire Part 1 investigation.

Current state

  • aos --versionUnicity AOS 2026.9.2
  • ~/.aos/runtime/bin/astrid --versionastrid 2026.9.2
  • distributions/unicity-ce/Distro.toml → 22 capsules, correctly repointed at releases/2026.9.2
  • Daemon: not running. Blocked on retains audit source after its importer.
  • astrid-storage-provider-fskit: still missing.