aslice — A Package Manager for Intel macOS

State, identity, privilege, and recovery contracts: STATE-AND-RECOVERY. Protected-volume patching: SYSTEM-VOLUMES. These specifications do not establish completed implementation or platform validation.

Name. aslice — an apple slice: a nod to the Macintosh apple and to the shape of the project itself. Binary packages are slices; formula repositories are orchards; the manager picks slices off the orchard, prebuilt or baked to order. The vocabulary is kept distinct from Homebrew's beer terminology to avoid community confusion and trademark friction. The project name is styled lowercase everywhere, including sentence starts — like the command.


Contents - [1. Context and Opportunity](#context-and-opportunity) - [2. Goals and Non-Goals](#goals-and-non-goals) - [3. Design decisions](#design-decisions) - [4. Platform Matrix and Microarchitecture Strategy](#platform-matrix-and-microarchitecture-strategy) - [5. Core Architecture](#core-architecture) - [6. Package Format](#package-format) - [7. The Variant and ABI Model — Interoperability by Design](#the-variant-and-abi-model--interoperability-by-design) - [8. Store, Profiles, and Generations](#store-profiles-and-generations) - [9. Distribution and the Build Farm](#distribution-and-the-build-farm) - [10. Security Model](#security-model) - [11. Performance Model](#performance-model) - [12. CLI and User Experience](#cli-and-user-experience) - [13. Policies, Governance, and Migration](#policies-governance-and-migration) - [14. Roadmap](#roadmap) - [15. Risks and Open Questions](#risks-and-open-questions) - [Appendix A. Specification status](#appendix-a-specification-status) - [Appendix B. References](#appendix-b-references)

1. Context and Opportunity

1.1 The gap that is opening

The Intel-Mac package-management ecosystem is losing its maintainer on a known schedule:

A large installed base remains: Homebrew's own analytics, discussed publicly in mid-2026, put Intel at roughly a quarter of active Homebrew Mac installations. Everything from El Capitan to Monterey is now outside Homebrew's support window — and precisely where these machines are stranded.

1.2 Who the users are

Three populations, all underserved:

  1. Owners of 2012–2020 Intel Macs used as daily drivers, home servers, audio rigs, and build machines. Many are maxed-out machines (Mac Pro 2013, iMac 5K, 16" MBP 2019) that remain genuinely capable.
  2. CI and legacy-maintenance shops that must keep building and testing x86_64 macOS software through Tahoe's support window.
  3. Retro, audio, lab, and 32-bit-dependent environments pinned to older releases. Catalina dropped 32-bit app support entirely; 10.11–10.14 are the last releases that run 32-bit software, classic audio drivers, and legacy pro tools — which is exactly why their users stay. Vendor-binary packages (§12.4) meet them where they live: 32-bit and universal pkg/dmg payloads install cleanly on precisely these releases.

1.3 Project scope

The project specifies package maintenance for Intel macOS 10.11–12: binary distribution, local build choices, explicit artifact bindings, and recoverable managed state. Support is established per platform configuration by implementation tests and recovery drills. Coexistence and migration are described in §13.3.

1.4 Design thesis

Target a fixed OS range, record actual CPU requirements, distinguish compatibility from artifact identity, and validate every execution and recovery boundary.

Each clause is developed in its own section: the platform axes in §4, the variant model in §7, the security model in §10.


2. Goals and Non-Goals

2.1 Goals

2.2 Non-goals


3. Design decisions

aslice targets a fixed range of Intel macOS releases. Its specifications make the following choices; implementation and platform acceptance remain required.

Concern Mechanism
Package execution Declarative TOML and sandboxed Starlark builds; binary installation is payload-only unless an explicitly approved graft is declared (§6; §12.15)
Build choices Compatibility keys select candidates; exact artifact identities bind bytes, flags, dependencies, and execution requirements (§7)
Installed state Immutable artifacts and generations, with journaled recovery for pointers and external effects (§8)
Ownership A private user-owned prefix; privileged execution uses independently verified protected closures and an authorized helper (§10.4)
Distribution A C++ client, authenticated indexes, and a self-hosted Intel build farm (§9; §11)
Privacy No telemetry or analytics of any kind, ever (§2.2 N7)
Vendor software Declared payload extraction, signer verification, and restricted graft execution where required (§12.4; §12.15)
Runtimes Session/project/default selection with exact artifact bindings and validated extension compatibility (§12.9)

4. Platform Matrix and Microarchitecture Strategy

4.1 The OS axis collapses — at 10.11

Seven OS releases and three flavors give 21 potential OS/flavor combinations. Deployment targeting can reduce build count, but each supported combination still needs runtime validation:

macOS has a mature deployment-target mechanism. The deployment target permits a binary to load on older releases; it does not prove runtime correctness. Newer APIs need availability handling, and each supported release needs tests. So the default remains: build once against the oldest target, per µarch flavor — the floor is 10.11 now instead of 10.15.

The lower floor has real consequences; each is handled explicitly:

4.2 The µarch axis grows: three flavors

Extending the floor to 10.11 pulls pre-SSE4 CPUs into the supported population, so the flavor space grows from two to three. aslice adopts the x86-64 psABI microarchitecture levels as its flavor vocabulary:

Flavor Level Key ISA Who needs it
v1 x86-64 baseline SSE2 Runs on every 64-bit Intel Mac; the only choice for Core 2 Duo machines (2007–2009, Merom/Penryn) found on 10.11–10.13
v2 x86-64-v2 SSE4.2, POPCNT Nehalem/Westmere and later — Mac Pro 2009+, most 2010+ Macs, and everything Catalina-capable
v3 x86-64-v3 AVX2, BMI2, FMA Haswell+ (2014→); 10–40% faster on codecs, crypto, compression, math

Notes:

4.3 Toolchain floor — self-hosted from day one

The authoritative toolchain document is TOOLCHAIN.md; this section is the rationale in brief.

Extending to 10.11 changes the toolchain story from "convenience" to "load-bearing":


5. Core Architecture

5.1 Process layout

aslice (single binary, unprivileged)
 ├── aslice-fetch     ── sandboxed helper: network + disk cache only
 ├── aslice-extract   ── sandboxed helper: archive extraction only
 ├── aslice-build     ── sandboxed helper: runs Starlark build scripts
 └── aslice-link      ── the only component that writes the store/profile

Privilege separation is structural: the helpers are separate executables (spawned by the main binary, which re-executes itself with a subcommand) running under Seatbelt profiles (§10.5) with only the capabilities their phase requires. The fetch helper can't touch the store; the extractor has no network; the linker has no network and no compiler. Each helper is small (a few hundred lines) and independently auditable — this is where the C++ attack-surface discipline pays for itself. aslice-extract is also the component that expands vendor .pkg (xar) and .dmg payloads (§12.4) — archive and installer-payload handling are the same trust problem and get the same tiny, fuzzed code path. The same helpers are what the build farm executes: the farm harness is an orchestrator that spawns aslice build jobs, and every build — farm or laptop — runs through this identical sandboxed executor (full design: BUILD-INFRA.md).

5.2 Major components

Component Responsibility Notes
Index client Fetches and caches the package index TUF metadata + zstd-compressed JSON snapshots; incremental updates via snapshot diffs, not git
Solver Version + variant resolution PubGrub-style CDCL algorithm over (name, version, variant) space; flavors as hard constraints (§7.5)
Store Content- and identity-addressed package trees /opt/aslice/store/<artifact-hex>/ (§8)
Profiles / generations Atomic merged views Symlink forests with rename-swap; pointer switching plus journaled recovery (§8.3)
Builder Fetch→unpack→patch→configure→build→install in sandbox Deterministic environment; DESTDIR staging; ABI scan on output (§7.3)
Verifier Signature, hash, ABI, and policy checks before linking Nothing reaches the profile without passing (§10.2)
Database Separate owner projections and disposable cache DATABASE specifies six SQLite roles and executable schemas; client state records installed state and durable decisions; trust/ separately owns security authority and the operation journal coordinates external effects (STATE-AND-RECOVERY §5 and STATE-AND-RECOVERY §7)
Reporter SBOM generation, audit, provenance display SPDX SBOM per package; OSV feed integration (§10.6)
Logger Structured operation and security-event logging JSONL on disk under log/, human rendering on the terminal; local-only, forever (§12.5)
Shim resolver Version selection for multi-version runtimes Multicall argv[0] dispatch; session → project → default resolution; exec-only, no wrapper process (§12.9)
Self-updater Updates aslice itself through the package machinery aslice is package zero: TUF-verified slice, generation swap, supervised activation with retained recovery entry point, automatic rollback on failure (§12.12)

5.3 Why C++ — and what it costs

The user-facing case for C++ is startup time, single-binary deployment across 10.11–12 with no runtime story, direct Mach-O/dyld/Seatbelt API access, and the profiler, sanitizer, and fuzzer tooling the performance work of §11 needs. The cost is memory safety, which is a security-goal liability. aslice treats it as an engineering constraint:


6. Package Format

6.1 Formulae are data, with a hermetic build script

The authoritative schema is PACKAGE-FORMAT.md; this section is the guided tour.

An aslice package is a directory in an orchard — a git repo of formula directories (what Homebrew calls a tap):

orchards/extended/ffmpeg/
 ├── package.toml      # metadata, sources, dependencies, variants
 ├── build.star        # Starlark build script (sandboxed, no IO escape)
 ├── patches/          # optional, checksummed
 └── tests.star        # optional smoke tests

package.toml:

This excerpt illustrates recipe structure; abbreviated hashes and omitted policy fields must be completed and validated before publication.

spec = 1

[package]
name        = "ffmpeg"
version     = "7.1.0"
revision    = 0
license     = "LGPL-2.1-or-later"
description = "Play, record, convert, and stream audio and video"
homepage    = "https://ffmpeg.org"

[[source]]
url    = "https://ffmpeg.org/releases/ffmpeg-7.1.tar.xz"
sha256 = "<verified-source-sha256>"
# mirrors = ["https://mirror.example/..."]   # optional fallback mirrors

[variants.x265]          # feature variant
default = true
abi     = true           # changes exported interface → part of build identity (§7)
description = "HEVC encoding via x265"

[variants.debug]
default = false
abi     = false          # ABI-neutral choice; artifact identity still records output
description = "Build with debug symbols"

[depends]
runtime = ["x264", "x265?variant.x265", "lame", "opus", "srt"]
build   = ["nasm", "pkgconf"]

build.star (Starlark: deterministic, no network, no filesystem access outside the build dir, no eval):

def configure(ctx):
    args = [
        "--prefix=" + ctx.prefix,
        "--enable-gpl",
        "--enable-libx264",
    ]
    if ctx.variant("x265"):
        args.append("--enable-libx265")
    ctx.env.append("CFLAGS", ctx.user_cflags)   # user flags honored, recorded, non-ABI (§7.4)
    ctx.run("./configure", *args)

def build(ctx):
    ctx.make(jobs = ctx.jobs)

def install(ctx):
    ctx.make("install", destdir = ctx.staging)

Key properties:

6.2 Binary package format (.slice)

A binary package — a slice — is one Zstandard frame containing a POSIX pax tar archive:

ffmpeg-7.1-0+extended.v3.2f4a9c1e.slice
 ├── slice.json        # container version, manifest digest/length, payload totals
 ├── manifest.json     # identity, ABI contract, file hashes, dependency bindings
 └── payload/          # normalized file tree

The slice format specification defines byte layout, normalization, limits, and extraction checks. Its container schematic and artifact manifest schematic define the JSON structures. Package signatures, SBOMs, and provenance are separate authenticated objects bound to the archive or artifact digest.

Installation authenticates metadata, the complete archive digest, and its detached signature before bounded extraction into staging. It checks the manifest and payload hashes, applies declared relocations, verifies ABI requirements, and activates the artifact through the transaction journal. Approved grafts run in an isolated staged view (§12.15). A repackaged vendor binary uses the same container; its authenticated provenance records the vendor digest and signer, while its recipe binds the repackaging procedure.


7. The Variant and ABI Model — Interoperability by Design

Compatibility and artifact identity answer different questions. The compatibility key describes the declared ABI and platform contract; the artifact identity binds the resulting bytes and exact dependencies. Substitution requires evidence beyond key equality.

7.1 The three kinds of build-time choice

Kind Examples Effect on identity Effect on interop
µarch flavor v1 / v2 / v3 Hard selection constraint ABI-identical; a higher-flavor binary won't run on lesser hardware
Optimization flags -O2/-O3, -march=native, LTO Same compatibility key only when ABI-neutral; distinct artifact identity Actual CPU requirements and ABI evidence are checked
Feature variants +x265, +ssl vs +gnutls, +shared Compatibility key changes when abi = true; artifact identity binds every output Changes exported interface → tracked in the ABI contract

7.2 Build identity

build_id is the full lowercase hexadecimal SHA-256 compatibility key defined in STATE-AND-RECOVERY §1. It includes repository identity, package version/epoch/revision, ABI variants, runtime ABI epoch, flavor, minimum OS, and toolchain identity. Vendor identities bind the vendor digest and set compiler/flavor fields to null.

Optimization choices may share a compatibility key, but byte-distinct outputs have distinct artifact_id values and immutable store addresses. Exact flags, dependency bindings, recipe digest, and actual CPU requirements are recorded in the artifact manifest. Equality of compatibility keys selects candidates; the ABI and CPU checks still decide whether substitution is allowed. Locks and generations select exact artifacts, never merely a compatibility key.

7.3 The ABI contract (the Mach-O insight)

The ABI scanner records each dylib's install name, current and compatibility versions, exported symbols, client symbol requirements, architecture, and evidence quality. The loader-version comparison is provider current_version >= client required compatibility version; a symbol hash establishes equality, not subset coverage. STATE-AND-RECOVERY §2 defines the comparison and its limits.

Exact dependency artifact bindings are the safe default. Automatic substitution requires adequate evidence plus dependent tests; stripped C++ interfaces and undeclared dynamically loaded plugins cannot be declared compatible from symbol names alone. Unknown evidence means retain the original provider or rebuild and test the dependent. Rebinding an absolute install name is an explicit rebuild or verified relocation producing a new artifact, not a profile switch.

This model permits tested combinations of local and prebuilt artifacts without claiming that all ABI or behavioral breakage can be detected before execution.

7.4 User flags

aslice install extended:ffmpeg --variant +x265 --cflags="-O3 -march=native" --lto

7.5 The solver

Version and variant resolution uses a PubGrub-style CDCL algorithm:

The prebuilt variant domain per package is small by policy (§13.2: the farm builds defaults plus demonstrated-demand variants, and everything else compiles locally), which bounds farm work while preserving local build choices.


8. Store, Profiles, and Generations

8.1 Layout

/opt/aslice/
 ├── store/
 │    ├── <artifact-hex-a>/                    # ffmpeg v3
 │    ├── <artifact-hex-b>/                    # ffmpeg v2; flavors coexist
 │    ├── <artifact-hex-c>/                    # x264 v3
 │    └── …
 ├── apps/         # vendor-binary .app bundles (§12.4)
 ├── profiles/
 │    ├── default -> generations/42             # symlink; the live view
 │    └── generations/
 │         ├── 41/  { bin/, lib/, share/, … }   # symlink forests into store
 │         └── 42/
 ├── cache/        # slices, sources, index snapshots
 ├── log/          # structured operation logs (§12.5)
 ├── trust/        # authoritative trust records; not reconstructed from SQLite
 ├── records/      # durable choices and compact operation history
 ├── cache/db/cache.sqlite # disposable verified projections and solves
 ├── db/state.sqlite # client state; other owners have separate databases
 └── etc/aslice.toml

8.2 Profiles as the interoperability surface

A profile is the merged symlink forest (bin/, lib/, share/, …) that users put on PATH: /opt/aslice/profiles/default/bin. Linking is nothing but symlink creation into a generation directory, so any combination of store paths — prebuilt, user-compiled, different flavors, old and new versions of different packages — coexists under one view. Collisions (two packages shipping bin/foo) are first-class: the profile records priority, and aslice profile prefer flips it without touching the store.

8.3 Generations: atomic switching and rollback

Package views switch through an atomic symlink rename. Whole-machine changes additionally use the durable journal and recovery state machine in STATE-AND-RECOVERY §5: serialize mutations, persist before-images, apply external changes, activate pointers, reconcile SQLite, then commit before bounded service health checks. Effective mutation ownership continues through those checks and survives parent death while helpers still write. A rename alone is not an atomic transaction across these systems.

aslice rollback [generation] and switch-generation execute journaled transitions to retained managed state. External edits stop inverse writes with a conflict; application databases and user data are outside package rollback. Changed service declarations restore their plists too. Protected-volume changes follow SYSTEM-VOLUMES and may require Recovery and a reboot. Recovery and active transactions retain all required artifacts against GC.

8.4 Garbage collection discipline

Keep the last 5 generations by default. gc.store_watermark is the store limit (default 20 GB); gc.warning_margin_percent is the warning margin below it (default 10, range 0–100). Warn when usage reaches limit × (1 - margin / 100). At or above the limit, ask Run garbage collection? [y/N]; No is the default. Never run GC automatically from the size check. Non-interactive checks print the warning and the aslice gc remedy without collecting. Explicit aslice gc remains available; aslice gc --dry-run shows what would be collected and why. Collection retains every root required by STATE-AND-RECOVERY §2 and §5; a threshold does not make reachable artifacts collectible.

Installed-on-request tracking. Every DB install record carries on_request = true|false — whether the user named the package or the solver pulled it in. aslice autoremove collects nothing reachable from an on_request root, cross-checked against retained generations, and aslice mark <pkg> --on-request/--as-dependency repairs the record when the user disagrees with it. Package holds live here too: aslice pin <pkg> / unpin record the hold in the same table, upgrade skips held packages, and outdated says so rather than hiding them.

Cache eviction. The GC above owns the store; the cache — downloaded slices, source tarballs, index snapshots, ccache — is owned by aslice clean: LRU eviction of anything not referenced by an installed package or retained generation, watermark-driven (default: evict when the cache exceeds 10 GB, never touch anything younger than 30 days), with --dry-run symmetry to gc. Source tarballs are the sleeper category: --build-from-source users accumulate them silently.

8.5 Shims: the multiplexing layer

Profiles have one structural limitation: a name like bin/php can point at only one store path per generation. That is correct for libraries — the profile is the interop surface and ambiguity there is a bug — but wrong for runtimes, where several versions installed at once is the normal state of a working machine. aslice resolves it with a thin shim layer (§12.9): a directory of multicall shims that sits before the profile on PATH and multiplexes versioned tools according to session, project, and default selections. Shimmed names are not linked into generations at all; the profile instead links versioned aliases (bin/php8.4) for services and scripts that must name an exact runtime. The store, generations, and rollback semantics are untouched — a shim only ever chooses among already-installed store paths; it creates no state the generations don't own.


9. Distribution and the Build Farm

9.1 Hosting on GitHub — two layers, mirror-friendly

Layer 1: Package blobs as OCI artifacts on GHCR. Slices are pushed to ghcr.io/aslice/<name> as OCI artifacts (ORAS), giving content-addressed blob storage, deduplication of byte-identical blobs (a monolithic changed archive does not automatically share content across versions), resumable/ranged downloads, and free bandwidth within GitHub's generous registry limits. Every tag is additionally anchored to a signed manifest digest.

Layer 2: The index as static, signed files. The package index (TUF metadata + zstd JSON snapshots) is published both to a GitHub Release asset stream and to raw/Pages endpoints, and — critically — is trivially mirrorable: any static HTTP server can host a complete aslice repo. Mirror support is a first-class config (mirrors = [...]), not an afterthought, because the long-term health of a legacy-platform project cannot depend on one vendor's continued generosity.

Fallback: plain GitHub Releases assets (2 GB per asset ceiling — no package comes close) for environments where GHCR auth/rate limits are a problem. The client treats GHCR, Releases, and static mirrors as interchangeable transports for identical, identically-signed content. All three are transports for the canonical distribution unit — the repository tree (§9.6): a static, signed, mirrorable directory of TUF metadata, index snapshots, formula metadata, and blobs. GHCR is where blobs may live; a repository is what a client consumes.

9.2 The GitHub CI problem

GitHub-hosted Intel runners are a deprecating asset: macos-11/macos-12/macos-13 images are already retired (the last of them in December 2025), and the replacement macos-15-intel label — the final hosted Intel runner — is available only until August 2027. Any design whose correctness depends on hosted Intel CI is a dead design. aslice therefore treats GitHub CI as a convenience layer and the self-hosted farm as the system of record.

9.3 The build farm

The harness that runs on this hardware — identical to what runs on a user's machine — is specified in BUILD-INFRA.md: job manifests, scheduling lanes, the quarantine/signing trust model, community evidence builders, and VM matrix orchestration. This section is the hardware summary.

Phase A (launch): hosted CI supplies optional coverage where its detected CPU and OS capabilities permit. The owned farm below is the system of record. Generating v3 machine code does not establish that a complete package build succeeds on a weaker CPU: configure probes, generated tools, dependencies, and tests may execute that code during the build. In the owned farm, complete v3 jobs belong exclusively to the laptop; VMs on the Mac Pro cannot add v3 execution support.

Phase B: the two owned Macs, using the standalone farm harness with an optional self-hosted CI adapter:

Role Hardware Notes
Primary v1/v2 builder and tester 2013 Mac Pro (trashcan) Also coordinator, compatible OS-test VMs, and restricted publisher VM
On-demand v3 builder and tester 2015 MacBook Pro Additional v1/v2 builds and independent rebuild checks; compatible v3 test guests

No new hardware purchase is required. CPU configuration, RAM, storage, and concurrency remain unspecified until measured. Core 2 Duo testing is optional future coverage outside current inventory. The seven-release OS matrix runs in batches; host/hypervisor/guest compatibility and guest-visible CPU features must be validated before claiming coverage (BUILD-INFRA §8).

When the laptop is absent, v3 work queues while eligible v1/v2 work continues. Missing tests and required independent rebuilds remain pending; all merge, signing, and atomic publication gates remain intact. The two Macs can independently cross-check v1/v2, but supply only one physical v3 builder (BUILD-INFRA §7.3).

The signing host is a dedicated networked release Pi; a separate offline Pi holds the root (§10.2). The Mac Pro's restricted publisher VM automatically delivers authenticated candidates and atomically publishes verified signatures; it holds only the timestamp key among repository signing keys. Build guests have no publication credentials. Shared-host compromise can corrupt evidence and publication and expose the timestamp key, but does not directly expose offline keys. Losing the Mac Pro stops preparation and publication until recovery (BUILD-INFRA §11).

9.4 What gets prebuilt

9.5 Build provenance

Every slice has a separate authenticated SLSA-style provenance attestation binding its artifact and archive digests: builder identity, source hash, formula commit, toolchain, build environment, and reproducibility evidence. Variable provenance is not part of the canonical identity manifest (STATE-AND-RECOVERY §1; SLICE-FORMAT §2). aslice provenance extended:ffmpeg shows it. Independent rebuild comparisons use the unsigned/normalized evidence contract in STATE-AND-RECOVERY §10; served byte-changing signatures precede final artifact identity. Verified reproducibility evidence supports the reproducible: true index badge.

For vendor-binary slices (§12.4) the provenance section instead records: the vendor artifact URL and sha256, the pinned signer identity and notarization state at pack time, the repackaging tool version, and whether the payload is hosted or vendor-fetched.

9.6 The repository system

GHCR, Releases, and static mirrors are transports. The canonical distribution unit — what a client consumes — is the aslice repository: a self-contained, signed, static tree:

repo.example.org/
 ├── tuf/            # root.json, snapshot.json, timestamp.json, targets.json
 ├── index/          # zstd JSON snapshots + diffs (the solver's world)
 ├── formulas/       # resolved package metadata (pure data; never executable)
 └── blobs/sha256/   # slices and vendored sources, content-addressed

Index retrieval, bounded diffs, staged activation, mirror failures, and transaction-scoped repository availability follow REPOSITORIES. Cross-namespace virtual providers, aliases, and replacements require retained explicit selection; repository names never transfer authority.

10. Security Model

The security design separates recipe execution, artifact authentication, ordinary prefix ownership, and privileged effects. Each boundary has explicit authorization and acceptance requirements.

10.1 Declarative packages, hermetic builds

10.2 Signatures and repository integrity (TUF)

10.3 Trust bootstrapping

Authenticated HTTPS delivers the online installer and its pinned bootstrap inputs. The verified bootstrap binary performs signature verification; an unverified downloaded verifier is never executed. A second transport supplies redundancy, not independent authority.

For TLS-dead machines, transfer a bootstrap kit from a supported machine and verify its SHA-256 using the stock shasum against an independently authenticated digest before execution. HTTP is permitted only for subsequent artifacts whose authentic pins are already available. Without an authentic kit or installer, bootstrap stops. STATE-AND-RECOVERY §7 defines both paths and their acceptance checks.

The installer may use sudo once to create the user-owned /opt/aslice prefix, or use ~/.aslice without elevation. Privileged features subsequently use a separately protected root and explicit authorization (§10.4).

10.4 Privilege discipline

10.5 Sandboxed builds

Every build phase runs under a Seatbelt (sandbox-exec) profile — Seatbelt predates the entire 10.11–12 window and is present on every supported release:

Phase Profile
fetch Network to declared hosts only; write to cache dir only
unpack/patch/configure/build No network at all; write only within the build dir; read-only toolchain and store
install (to staging) No network; write to staging dir only
test No network by default; opt-in test_network = true per formula, logged at warn

Farm package-controlled execution uses disposable VMs in addition to these in-guest profiles, including official recipes, PRs, autobumps, and third-party orchards. Credentials remain outside the guest; BUILD-INFRA defines the isolation and cache boundary. Local source builds retain the same input verification and sandbox requirements; farm isolation is not established by a passing schema.

Vendor-binary payload extraction (xar expansion, hdiutil attach, cpio unpack) runs under the unpack profile — no network, writes confined to staging. The one phase in which a vendor artifact gets to run anything is the approved graft, which executes under its own manifest-derived profile (§12.15).

Seatbelt is deprecated by Apple on newer releases but frozen-in-place across our entire (frozen) target window; the profile abstraction (SandboxPolicy compiled to Seatbelt today) is designed so a future backend can replace it without touching formulae. A build that escapes its profile fails the build and files an automatic audit event.

10.6 Vulnerability and SBOM pipeline

The closed advisory schema defines each field. DNF security/minimal selection, DNF restart reporting, and Zypper security patches are upstream precedents; aslice's exact bindings and macOS coverage rules above remain its own contract. - Formulae declare upstream security-contact and EOL policy; packages past upstream EOL are surfaced in audit and require --allow-eol to install.

Security remediation invalidates affected transitive consumers on recipe, source, toolchain, build-configuration, or selected dependency changes. ABI compatibility remains a gate and does not waive remediation. The recipe graph and exact artifact bindings are separate evidence; BUILD-INFRA specifies closure construction and atomic publication.

10.7 What this does not solve


11. Performance Model

These are unmeasured performance targets and proposed mechanisms, not established results:

Goal Mechanism
CLI startup < 10 ms Single Mach-O binary, static libc++, no interpreter, no JIT, lazy dyld binding, no network on the hot path
install of a cached slice < 300 ms Verify (Ed25519: microseconds) → zstd decompress → APFS clonefile into store (HFS+ systems fall back to hardlink/copy) → symlink generation swap. No relocation pass on default prefix.
Index update < 200 ms typical Snapshot diffs against a cached snapshot hash — a few KB on a typical day
Solve < 50 ms typical SQLite-backed package index with prepared statements; PubGrub with clause caching; memoized per snapshot
Downloads saturate the pipe HTTP/2 multiplexing, bounded parallel fetches and decompression, resumable ranges, Zstandard payloads
Cold full install of a large tree (e.g., ffmpeg closure) < 10 s on SSD Parallel fetch + pipeline overlap (decompress stream N+1 while linking N)
Builds: near-zero manager overhead The builder's job is to get out of the way: Ninja parallelism, ccache-compatible compiler cache in cache/, tmpfs-backed build dir when RAM allows
Shim dispatch < 1 ms Multicall binary (no interpreter, no JIT), committed execution catalog and validated admission, exec instead of fork. This is a target requiring measurement with closure validation and recovery gates enabled (§12.9)

Flavor targeting may improve some workloads, but no advantage is claimed until measured with identical inputs and compatible hardware. OCI storage shares identical blobs only; binary deltas, metadata sharding, and reusable prepared build environments are later experiments. Reusing an environment must still verify all inputs and create disposable execution instances.

Benchmark startup, immutable full/diff index refresh, solving, cached and cold installation, shim dispatch with admission checks, VM preparation, and security rebuild closures. Publish versioned workload manifests with exact inputs: small single-package and large dependency closures; satisfiable/conflicting variant solves; static/header/bundled security changes; cold and warm caches. Record hardware and guest identities, OS, CPU features, RAM, storage/filesystem, network conditions, package counts and compressed/uncompressed sizes, cache state, concurrency, repetitions, failures, peak memory, and median/p95/p99 latency. Include APFS and HFS+ runs and verification costs. Store raw farm measurements and harness versions; collect no user telemetry. Qualification and benchmark execution remain pending.

Transfer and decompression workers share explicit connection, memory, expanded-byte, disk-watermark, and CPU budgets. Start with at most eight transfers and two decompressors, reducing concurrency to fit measured memory/disk budgets; refuse a job whose single-object bound cannot fit. Enforce bounds before allocation and during streaming, cancel safely on overflow, and report the limiting resource. Values are initial limits to validate, not throughput claims.


12. CLI and User Experience

12.1 Commands

Space-separated mixed-orchard packages form one transaction; pre-commit failure rolls back the managed-state batch. Package-specific options identify their target (for example --variant extended:ffmpeg:+x265); reject ambiguous batch options and display effective settings per package. Stateful --for scoping is superseded.

Busy interactive commands offer wait or exit with owner information; unattended waiting requires explicit authorization. After waiting or recovery, revalidate and obtain confirmation for material changes. The initiating user or an authenticated administrator can request stopping: attempt rollback before commit; after commit, stop checks safely and report incomplete verification. See STATE-AND-RECOVERY §5.

The index covers specified public commands, including maintainer and farm operations. Historical spellings and internal helper executables are excluded. These interfaces remain specifications, not a claim of completed implementation. Each family page defines its arguments, options, examples, limits, and applicable exit statuses; details not yet specified are identified there.

OPTIONS refers to the owning page. Database and recovery SELECTORS mean --role ROLE and either --prefix PATH or --instance ID; selection grants no authority. Global verbosity, JSON/log rendering, and authorized lock waiting are documented in aslice(1). A dry-run is supported only where its command contract says so.

Package management

Syntax Purpose and manual
aslice install PACKAGE… [OPTIONS] Install the newest eligible packages; disclose source builds. aslice-install(1).
aslice reinstall PACKAGE… [OPTIONS] Relink the same version to repair profile entries. aslice-install(1).
aslice upgrade [PACKAGE…] [--security [--minimal]] [--allow-source-builds] Update all or selected packages; security modes retain unresolved findings. aslice-upgrade(1).
aslice outdated [--json] Show proposed updates and package holds. aslice-upgrade(1).
aslice uninstall PACKAGE… Remove packages from future generations. aslice-uninstall(1).
aslice autoremove [--dry-run] Remove dependencies no longer needed by requested packages. aslice-uninstall(1).
aslice mark PACKAGE --on-request\|--as-dependency Repair the requested/dependency record. aslice-uninstall(1).
aslice pin PACKAGE Hold a package against upgrades (one argument). aslice-uninstall(1).
aslice unpin PACKAGE Release an upgrade hold. aslice-uninstall(1).
aslice plan install PACKAGE… [OPTIONS] Resolve a request to a saved plan without executing. aslice-apply(1).
aslice lock export Write the current exact resolution to stdout. aslice-apply(1).
aslice apply DOCUMENT [--dry-run] [OPTIONS] Execute a plan or replay a lock; DOCUMENT may be an HTTPS URL. aslice-apply(1).
aslice adopt --from-homebrew Produce a migration plan from the installed Homebrew leaf set. aslice-adopt(1).
aslice graft approvals [--json] Review recorded script approvals. aslice-graft(1).
aslice graft revoke PACKAGE… Withdraw approvals; a later install asks again. aslice-graft(1).

Inspection

Syntax Purpose and manual
aslice search QUERY Match package names and descriptions. aslice-inspect(1).
aslice info PACKAGE Show versions, variants, dependencies, notes, and provenance. aslice-inspect(1).
aslice flavors PACKAGE Show the prebuilt matrix for this machine. aslice-inspect(1).
aslice leaves [--user-built] List packages without installed dependents. aslice-inspect(1).
aslice why PACKAGE Explain installed dependents. aslice-inspect(1).
aslice provenance PACKAGE Show authenticated build or repackaging evidence. aslice-inspect(1).
aslice audit Report known vulnerabilities in the installed set. aslice-inspect(1).
aslice needs-restarting [--json] Report restart, rebuild, reboot, and unknown inspection coverage. aslice-needs-restarting(1).

Profiles and runtime selection

Syntax Purpose and manual
aslice history List retained generations and attributed changes. aslice-profile(1).
aslice rollback [GENERATION] Journal restoration of retained managed state. aslice-profile(1).
aslice switch-generation GENERATION Select a retained generation explicitly. aslice-profile(1).
aslice link PACKAGE Expose an installed package in the profile. aslice-profile(1).
aslice unlink PACKAGE Retract profile exposure without removing dependency bindings. aslice-profile(1).
aslice profile prefer NAME PROVIDER Select a colliding name or virtual provider. aslice-profile(1).
aslice exec PACKAGE -- COMMAND [ARGUMENT…] Run in a temporary profile view. aslice-profile(1).
aslice exec --replacement PATH -- PACKAGE COMMAND [ARGUMENT…] Explicitly run a verified isolated replacement closure. aslice-profile(1).
aslice use RUNTIME STREAM [--install] Select a stream for the current shell through emitted shell code. aslice-use(1).
aslice use RUNTIME --clear Clear the session selection. aslice-use(1).
aslice pin RUNTIME STREAM [--install] Write a project runtime pin (two arguments). aslice-use(1).
aslice default [RUNTIME [STREAM]] Set or inspect profile-wide fallback selections. aslice-use(1).
aslice versions RUNTIME Show installed streams, selections, and extensions. aslice-use(1).
aslice which RUNTIME Trace runtime resolution to the store path. aslice-use(1).

Maintenance and recovery

Syntax Purpose and manual
aslice gc [--dry-run] [--older-than 30d] Collect unreachable store artifacts. aslice-gc(1).
aslice clean [--dry-run] Evict eligible cache entries. aslice-gc(1).
aslice store verify [--quarantine PACKAGE] Rehash artifacts and optionally quarantine a mismatch. aslice-gc(1).
aslice doctor [--fix] [--json] [--brief] [--deep] [--offline] Inspect health; fix only the documented no-data-loss cases. aslice-doctor(1).
aslice log [--last-op] [--follow] [--level LEVEL] Query local operation logs. aslice-doctor(1).
aslice db list [--role ROLE] [--json] List configured visible database instances. aslice-db(1).
aslice db [SELECTORS] schema [--live] Show shipped DDL or inspect the live schema. aslice-db(1).
aslice db [SELECTORS] query SQL [--json] Run one bounded read-only query. aslice-db(1).
aslice db [SELECTORS] check [--json] Validate identity, integrity, references, and records. aslice-db(1).
aslice db [SELECTORS] maintain [--dry-run] [--json] Run bounded maintenance and report deferrals. aslice-db(1).
aslice db [SELECTORS] compact [--dry-run] [--json] Reclaim file space through a validated versioned copy. aslice-db(1).
aslice db [SELECTORS] backup DESTINATION [--json] Write an owner-authorized coordinated backup set. aslice-db(1).
aslice db [SELECTORS] restore SET [--dry-run\|--confirm DIGEST] [--json] Preview and explicitly authorize restoration. aslice-db(1).
aslice recover [SELECTORS] [--continue\|--manual\|--salvage\|--activate] [OPTIONS] Guide recovery; unattended actions bind to a reviewed plan digest. aslice-recover(1).
aslice operation status [--json] Inspect owner, phase, helpers, and recovery state. aslice-recover(1).
aslice operation stop [--operation-id ID] [--json] Request authorized safe stopping; ID is required unattended. aslice-recover(1).
aslice self-update [--check] Update the manager with retained recovery capability. aslice-self-update(1).
aslice decommission [--dry-run] Restore managed external effects before removing the prefix. aslice-self-update(1).

Repositories and authoring

Syntax Purpose and manual
aslice repo add URL [--trust local] Add a signed repository and establish trust. aslice-repo(1).
aslice repo list [--sources-diff] [--json] List repositories. aslice-repo(1).
aslice repo enable NAME Enable a listed source. aslice-repo(1).
aslice repo disable NAME Disable a source. aslice-repo(1).
aslice repo remove NAME Remove a configured source. aslice-repo(1).
aslice repo re-pin NAME Replace key authority after independent verification. aslice-repo(1).
aslice repo keys NAME Inspect keys. aslice-repo(1).
aslice repo audit NAME Inspect trust, countersignature, and staleness. aslice-repo(1).
aslice repo allow-system-patch NAME Grant a verified repository patch permission. aslice-repo(1).
aslice repo deny-system-patch NAME Revoke its patch permission. aslice-repo(1).
aslice repo build PATH Compile an orchard or manifest directory into a repository. aslice-repo(1).
aslice repo sign PATH [--sign-with ed25519\|openpgp] Apply repository signing keys. aslice-repo(1).
aslice repo publish PATH Publish through the configured transport. aslice-repo(1).
aslice orchard add ORG/ORCHARD Follow a formula orchard. aslice-orchard(1).
aslice orchard pin ORG/ORCHARD COMMIT Pin an orchard to a commit. aslice-orchard(1).
aslice orchard lint [PATH] [--json] Validate every formula. aslice-orchard(1).
aslice orchard doctor [PATH] [--json] Report orchard health. aslice-orchard(1).
aslice orchard freshness [PATH] [--json] Report upstream freshness. aslice-orchard(1).
aslice orchard ci [PKG…] [--flavors v2,v3] [--all] Run local merge gates; unavailable farm tiers remain deferred. aslice-orchard(1).
aslice orchard dependents PKG [--transitive] [--json] Inspect reverse dependencies and rebuild impact. aslice-orchard(1).
aslice orchard deprecate PKG --reason REASON [--replacement PKG] --date DATE [--disable-date DATE] Declare lifecycle dates and reason. aslice-orchard(1).
aslice orchard disable PKG Bring the disable date forward to today. aslice-orchard(1).
aslice orchard undeprecate PKG Remove deprecation metadata. aslice-orchard(1).
aslice orchard tombstone PKG Remove the formula while retaining its index tombstone. aslice-orchard(1).
aslice orchard rename OLD NEW Deprecate as renamed and scaffold a successor. aslice-orchard(1).
aslice orchard port --from-homebrew FORMULA Translate a Homebrew formula into a reviewed draft. aslice-orchard(1).
aslice create URL Draft a formula from a source URL. aslice-author(1).
aslice lint FORMULA Validate one formula against schema and policy. aslice-author(1).
aslice build PACKAGE-OR-DIRECTORY [BUILD-OPTIONS] Compile locally with the farm harness; reproduction has its own form. aslice-author(1).
aslice build --reproduce PACKAGE VERSION FLAVOR BUILD-REFERENCE Compile locally with the farm harness; reproduction has its own form. aslice-author(1).
aslice test PACKAGE Run the installed slice smoke tests. aslice-author(1).
aslice livecheck [PACKAGE \| --all] Query upstream versions. aslice-author(1).
aslice bump-pr PACKAGE VERSION Edit, lint, smoke-build, and open a version-bump PR. aslice-author(1).
aslice farm plan Compute the affected build and test DAG. aslice-farm(1).
aslice farm coordinator Schedule work, gate, and collect evidence. aslice-farm(1).
aslice farm agent [--once] [--vm-guest] [--reproduce-only] Run a worker or guest test agent. aslice-farm(1).
aslice farm enroll --project URL Enroll an evidence worker and pin the coordinator. aslice-farm(1).

Machine setup

Syntax Purpose and manual
aslice machine apply [aslice-machine.toml \| https://…] [--dry-run] [--prune] [--accept-system-changes] [--accept-grafts] [--json] Converge a machine wishlist; default ./aslice-machine.toml. aslice-machine(1).
aslice machine export [--defaults DOMAIN,…] [--system-defaults DOMAIN,…] Capture managed state; preference domains are opt-in. aslice-machine(1).
aslice machine import --from-brewfile Brewfile Translate a Brewfile with a skip list. aslice-machine(1).
aslice service list List managed launchd services. aslice-service(1).
aslice service status PACKAGE Report launchd state and last exit. aslice-service(1).
aslice service start PACKAGE Start the declared service. aslice-service(1).
aslice service stop PACKAGE Stop the declared service. aslice-service(1).
aslice service restart PACKAGE Restart the declared service. aslice-service(1).
aslice service run PACKAGE Run in the foreground without registration. aslice-service(1).
aslice ca-update [--check] Refresh private trust or explicitly selected trust/crypto layers. aslice-ca-update(1).
aslice ca-update --keychain Refresh private trust or explicitly selected trust/crypto layers. aslice-ca-update(1).
aslice ca-update --keychain-remove Refresh private trust or explicitly selected trust/crypto layers. aslice-ca-update(1).
aslice ca-update --crypto Refresh private trust or explicitly selected trust/crypto layers. aslice-ca-update(1).
aslice ca-update --apple-certs Refresh private trust or explicitly selected trust/crypto layers. aslice-ca-update(1).
aslice ca-update --from-file bundle.pem Refresh private trust or explicitly selected trust/crypto layers. aslice-ca-update(1).
aslice system-patch list List managed replacements of Apple files. aslice-system-patch(1).
aslice system-patch status [PATH] Inspect baseline, hashes, drift, and pending reboot. aslice-system-patch(1).
aslice system-patch restore PATH [--accept-system-changes] Restore through the compatible OS backend. aslice-system-patch(1).
aslice system-patch prepare [--accept-system-changes] Persist protected recovery material before Recovery. aslice-system-patch(1).
aslice system-patch finalize [--accept-system-changes] Verify the booted result before committing. aslice-system-patch(1).

Shell and configuration

Syntax Purpose and manual
aslice shellenv Print profile paths and trust-store environment exports; no writes. aslice-shell(1).
aslice init SHELL Print integration for bash, zsh, or fish. aslice-shell(1).
aslice config get KEY Read configuration; detailed get behavior remains unspecified. aslice-shell(1).
aslice config set KEY VALUE Set a configuration key. aslice-shell(1).
aslice help COMMAND Print the owning man-page text. aslice-shell(1).

Representative choices retain their distinct meanings:

aslice install extended:ffmpeg                  # newest eligible version; flavor auto-detected
aslice install extended:ffmpeg --build-from-source
aslice install extended:ffmpeg --variant +x265 --cflags="-O3 -march=native"
aslice install extended:ffmpeg@v6               # version constraint
aslice install audiolab:convolver      # explicit repository namespace
aslice install php@8.4                 # coexisting runtime stream; does not select it
aslice install php-redis               # extension bound to the selected runtime
aslice install composer                # tool that rides the selected runtime
aslice pin openssl                     # one argument: package upgrade hold
aslice pin php 8.4                     # two arguments: project runtime selection
aslice upgrade extended:ffmpeg --rollback-on-service-failure
aslice upgrade --security
aslice upgrade --security --minimal --allow-source-builds
aslice install foo --accept-system-changes
aslice install protools-hd --accept-grafts
aslice plan install extended:ffmpeg > plan.json
aslice lock export > aslice.lock
aslice profile prefer blas openblas
aslice config set flavor v2
aslice init zsh

12.2 Interaction principles

12.3 Orchards, repositories, and trust levels

Orchards are git repos of formula directories — the authoring format. Repositories (§9.6) are the distribution format. Trust is explicit at both layers:

12.4 Vendor binaries (pkg/dmg) and GUI apps

Some software for this platform will only ever ship as a .pkg installer or a .dmg — commercial audio tools, vendor CLIs, frozen releases of abandoned apps. aslice installs it without running installer code by default — and with it only as a declared graft (§12.15):

Vendor binaries participate in the store, generations, profiles, lock files, and audit like source-built packages. Their build_id excludes flavor and toolchain (§7.2), and their payload dylibs get the same ABI scan at pack time — dependents link against vendor libraries through the same contract as farm-built ones. The scan is per-architecture: universal payloads record separate x86_64 and i386 ABI entries, and the x86_64 entry is what aslice-built dependents (always 64-bit) consume.

12.5 Logging and diagnostics

A package manager that fails opaquely trains users to fear it. aslice logs everything it does, to the local machine, and nowhere else — the charter (N7) applies to logs as it does to metrics: nothing is ever transmitted, aggregated, or phoned home, not even opt-in.

Where logs live. /opt/aslice/log/ (or ~/.aslice/log/ for per-user prefixes), one JSONL file per day, rotated and size-capped (default: keep 14 days or 256 MB, whichever is less; both configurable). The build harness keeps its own per-build structured logs (log.jsonl, BUILD-INFRA §4) — this section governs the client.

What gets logged. Every operation is a structured event stream with a generated operation ID that ties terminal output, log records, and the history table (REPOSITORIES §11) together:

Category Examples Level
Mutations install/upgrade/uninstall plans, generation swaps, rollbacks, GC runs info
Security events signature/hash/ABI verification results, key-pin changes, trust-level demotions, sandbox escapes, frozen repos warn or error — unsuppressible (--quiet cannot hide them)
Decisions overlap resolutions and revalidations (§9.6), fallback-to-source events, flavor downgrades, EOL acceptances info
Diagnostics network retries, mirror failovers, slow solves, disk-space pressure debug at -v, trace at -vv

The message-quality standard. Log messages are UI, and they are held to the same bar as the CLI itself:

Silence discipline. Steady-state success is quiet: a successful binary install prints its plan summary and result, and everything else lives in the log at info level. aslice never logs at warn for things that are fine (a lesson from tools whose warning noise trains users to ignore real ones).

12.6 Doctor: sanity checking

aslice doctor is the single entry point for "is my installation healthy?" — it runs a fixed battery of checks, reports each as pass/warn/fail with the actionable-message standard of §12.5, and never changes anything itself (repairs are explicit commands it recommends). It is read-only, offline-capable, and fast (< 1 s for the standard battery; deep checks are opt-in).

Check groups:

Group Checks Verdicts
Machine CPU flavor vs. configured flavor (a v3 config on v2 hardware is a fail, not a surprise SIGILL later); macOS release vs. supported window; APFS vs. HFS+ (capabilities that degrade, announced); free disk vs. GC watermark pass / warn / fail
Prefix and store Prefix ownership and permissions (user-owned, not world-writable); store path integrity — manifests re-hashed against on-disk content (--deep re-hashes every file, default checks a sample plus anything the DB flags); dangling store paths referenced by no generation pass / fail
Profiles and generations default symlink resolves; every profile symlink lands inside the store; the live generation matches the DB's installed set; collision priorities resolve to real paths pass / fail
Database SQLite integrity check; schema version vs. binary (a newer DB than the binary is a fail with downgrade instructions, never silent corruption); WAL recovery state pass / fail
Repositories Per repo: reachable (or cached-snapshot age if --offline), TUF metadata expiry countdown, pinned key still matches live root, trust-level consistency (a verified repo whose countersignature lapsed is a fail with the freeze explanation — REPOSITORIES §3), namespace uniqueness and qualified non-core names, index staleness beyond policy pass / warn / fail
Coexistence Homebrew/MacPorts presence, PATH ordering advice, anything in /usr/local shadowing aslice binaries (or vice versa) — advisory only, aslice never touches either pass / warn
Environment ASLICE_* variables that override config (listed, not hidden); shell init files referencing stale prefixes; Xcode CLT presence (informational — the self-hosted toolchain makes it optional for aslice itself) info / warn
Trust store ca-certificates bundle freshness against the index (a stale trust store is this platform's day-one failure); profile env wiring (SSL_CERT_FILE/CURL_CA_BUNDLE/GIT_SSL_CAINFO) points at the aslice bundle; System-keychain imported set matches the DB record — drift after OS updates or third-party cleanup reported, never silently repaired (§12.10) pass / warn
System patches Every declared [system-patch] target still symlinks into the live generation; backup files present and hash-matching the DB record; drift after macOS updates (a patch Apple restored, or a newer Apple file our symlink now shadows) reported with reapply/restore remedies, never silently re-patched (§12.11) pass / warn / fail
Grafts Every recorded graft footprint still matches the state DB: declared writes present (or recorded as user-removed), and nothing inside a graft's declared paths that the DB did not record — drift means something outside aslice touched the graft's territory, and the report names the paths (§12.15) pass / warn

Rules:


12.7 System software: kexts and SIP-disabled development tools

Some software this platform needs cannot live entirely inside the store: kernel extensions (audio-interface drivers, filesystems, hypervisors) and development tools that require SIP disabled (low-level debuggers, DTrace-based profilers, kernel instrumentation — a real population on 10.11–12 development machines). Earlier drafts rejected this category outright; v1.2 replaces the rejection with a declared path, because the software exists and users install it today by hand — with no provenance, no warnings, and no rollback. A package manager that refuses to see that protects no one.

Declaration. A package opts in via package.toml:

[system]
kexts            = ["Library/Extensions/FooAudio.kext"]  # payload-relative paths to install
sip_off_required = false     # true: the software cannot function while SIP is enabled
reason           = "Kernel driver for FooAudio USB interfaces"   # mandatory; this IS the warning text

Either kexts or sip_off_required = true (or both) marks a system package; reason is mandatory and shown verbatim in every warning. Development tools needing SIP off but installing no kext declare only sip_off_required and reason. The declaration is specified in PACKAGE-FORMAT under the system table.

Mechanism — declarative, elevated, still code-free. The category preserves the founding rules:

Warnings at every decision point. Declared system requirements surface at every decision point:

Trust gating. Serving system packages requires the system capability, granted by trust level (REPOSITORIES §3): official and verified repositories have it; third-party repositories never do; local repositories have it (a developer's own file:// tree on their own machine — the same authority as installing the kext by hand, now with warnings and rollback). Talking a user into installing a kernel extension is the social-engineering attack the trust levels exist to block, so no remote stranger's repository can offer one. Tier policy — extended by default, core only when the platform genuinely requires it — lives in ORCHARD-POLICY §13.


12.8 Services: launchd-native lifecycle and safe upgrades

Long-running services — nginx, PostgreSQL, Redis, dnsmasq, unbound — are where a package manager meets the running system, and they impose two requirements. The manifest must describe the service rather than ship scripts that manage it, and an upgrade must never replace the binary under a running process: stop the service, swap, start it again. aslice generates launchd declarations and includes stop–swap–restart in its journaled upgrade transaction.

Declaration. A package describes its service in package.toml (schema: PACKAGE-FORMAT §3.8):

[service]
run         = ["bin/nginx", "-g", "daemon off;"]  # argv, profile-relative; never a shell string
domain      = "user"        # "user" (default: gui/<uid> agent) | "system" (root LaunchDaemon — gated)
keep_alive  = true          # launchd KeepAlive — bool or a table of conditions
run_at_load = true
working_dir = "var"                   # prefix-relative
environment = { LANG = "en_US.UTF-8" }
log_dir     = "var/log/nginx"         # StandardOutPath / StandardErrorPath

aslice generates launchd plists. User jobs may resolve through the ordinary profile. Root jobs resolve only through the protected root-owned closure and pointer; their plist and execution-affecting configuration are root-controlled (STATE-AND-RECOVERY §3). Labels include prefix/profile identity as well as package name so concurrent profiles cannot control each other's jobs.

The command. aslice service is a thin layer over launchctl's modern interface (bootstrap / bootout / kickstart / print, present since 10.10, so the whole 10.11–12 window is covered):

Upgrades stop the service first. The mutation pipeline of §8.3 becomes service-aware whenever a plan touches a package with a loaded job:

  1. Resolve, fetch, and build the entire new generation while the old one — and the service — keeps running. Any failure here never touched the service.
  2. bootout the affected jobs — and only the affected ones; an ffmpeg upgrade never bounces your postgres.
  3. Swap the generation symlink (atomic, §8.3).
  4. Reconcile plists — only if the declaration changed; the profile indirection means a plain version bump needs no plist edit.
  5. Reconcile and commit the installation, then bootstrap / kickstart -k the jobs and run service-specific readiness checks, defaulting to 60 seconds per service with positive finite overrides. A PID alone proves only liveness. Retain mutation ownership through the checks. A job that won't start is an error carrying launchd's last exit status and the log path — and then aslice asks the user about rolling back (below).

A failed health check asks; it never decides silently. The failure is shown first — the job's launchd exit status and the log path — then, on an interactive terminal:

$ aslice upgrade nginx
…
error: installation committed; service nginx failed to start after the upgrade (launchd exit status 78;
log: /opt/aslice/profiles/default/var/log/nginx/error.log)
The previous generation (nginx 1.26.2, generation 41) is retained.
Rollback requires verified service-data compatibility or an authorized restoration procedure.
Roll back and restart the previous version? [y/N]

GC is already safe: a running job pins its store path through the profile generation it was started from, and §8.4 never collects store paths referenced by running processes.

Root daemons are the privileged case. domain = "system" jobs run as root (or a declared user_name) and are bootstrapped into the system domain by aslice-system (§10.4) — same per-operation consent, same unsuppressible logging as kexts. Because root execution is the privilege that matters, domain = "system" declarations are gated by the same system capability as [system] packages (REPOSITORIES §3): official and verified repositories may serve them, third-party repositories never may, local trees may on the user's own machine. User-domain agents are unprivileged and ungated — any repository may declare one, no sudo is ever involved, and the steady-state rule (§10.4) stands for nginx-on-8080 and every other development service.

Doctor. doctor.services checks: every enabled service's plist parses and its ProgramArguments resolve into the live generation; jobs loaded for packages no longer installed (and packages with declared services that were never enabled); crash-looping jobs (launchd's throttling state); and, for domain = "system" jobs, that what is running matches what the DB records aslice-system installed. All read-only; --fix offers only the no-data-loss repairs (§12.6).


12.9 Multi-version runtimes: use, pin, default — and version-bound extensions

Users may keep several php, nodejs, ruby, and python versions installed. The immutable store retains exact artifacts; a separate selection layer chooses the runtime for a session, project, or profile default. Services and compiled extensions retain explicit artifact bindings.

One formula, release streams. A runtime is a single formula (php) whose orchard publishes several maintained streams (8.3, 8.4, 8.5) in the index simultaneously. aslice install php@8.4 is an ordinary version-constrained install; the store happily holds 8.3.11, 8.4.13, and 8.5.0 side by side. What installing a stream does not do is change which php you get — selection is always explicit, never a side effect of installing. An upgrade remains within the selected stream; installing a new stream does not select it.

Recovery does not deny all shim dispatch. A separately retained committed execution catalog supplies defaults, installed streams, and exact closures independently of SQLite. Durable affected-selection gates precede live writes. Established unaffected selections and verified closures remain usable; unavailable required state fails promptly without choosing another runtime. Session/project/default precedence below stays unchanged. Explicit isolated replacement execution is separate from shim selection (STATE-AND-RECOVERY §5.2).

The shim layer. PATH gains one directory ahead of the profile: /opt/aslice/shims (the installer sets the order; doctor.coexistence verifies it).

A shim is a hardlink to the aslice binary dispatched on argv[0] — zero per-tool code — created for every name a runtime formula declares in shims = [...] (php, phpize, pecl, php-fpm; node, npm, npx; ruby, gem, bundle; python3, pip3, …). Invoked as php, the shim resolves a stream (below), validates its exact closure through the committed execution catalog and affected-selection gate, and execs the real binary — no fork, no wrapper process, signals and ps intact. The sub-millisecond dispatch target requires measurement with these checks enabled (§11). Shimmed names are not linked into generations; the profile links versioned aliases instead (bin/php8.4), which services and scripts use when they must name an exact runtime — §12.8's generated plists bind the alias of the stream selected at enable time, so aslice default php 8.5 never silently changes what a running php-fpm executes, and moving a service between streams is an explicit disable/enable. Resolution, first match wins:

  1. Session — ASLICE_USE_PHP=8.4 in the environment, set by aslice use.
  2. Project — the nearest aslice.toml walking up from the working directory, written by aslice pin.
  3. Default — the profile-wide selection recorded by aslice default in the state DB.
  4. Sole installed stream — if exactly one is installed, it wins; zero, or several with no selection, is an error naming the installed streams and the three ways to choose.

aslice use — the current shell. The command respects Unix semantics: a child process cannot rewrite its parent's environment, so aslice use php 8.4 emits shell code — export ASLICE_USE_PHP=8.4 on stdout, the human message on stderr. With the optional shell integration (eval "$(aslice init zsh)", a few auditable lines for bash/zsh/fish), an aslice shell function evals that automatically; without it, eval "$(aslice use php 8.4)" does the same by hand, ssh-agent-style. The session pin is an ordinary environment variable: it propagates to subshells, dies with the shell, overrides project and default, and shows up in env and in doctor's environment group like every other ASLICE_* override. aslice use php --clear unsets it. Implicit per-shell state without shell cooperation (PID-keyed databases, process-ancestry tricks) was rejected: tmux and daemon-spawned shells defeat ancestry, and a selection mechanism you cannot see with env is one you cannot debug. If the requested stream isn't installed, an interactive run offers to install it; non-interactive runs fail with the suggestion unless given --install.

aslice pin — the project. aslice pin php 8.4 writes (or updates) aslice.toml in the project root:

# aslice.toml — commit me alongside composer.json / package.json / Gemfile
[runtimes]
php    = "8.4"
nodejs = "22"

Every shell, subshell, CI step, and editor-integrated terminal inside that tree now resolves to the pinned stream — Volta's package.json behavior, made ecosystem-neutral (one file pins php and node in the same repo; no per-ecosystem pin formats). Pins record streams (8.4), never exact patches: patch movement within a stream is aslice upgrade's job and must not require editing a committed file. A pin naming a stream that isn't installed behaves like use in that situation: interactive offer, non-interactive error, --install to override. aslice which php traces the full resolution — session? project? default? — down to the store path, so "which php am I actually running, and why" is one command, the same explainability standard as --explain for the solver.

aslice default — the fallback. aslice default php 8.4 journals the profile-wide selection and updates the state DB and committed execution catalog; aslice default php shows it; bare aslice default lists all selections. The default is what cron jobs, services, and shells outside any project tree land on. Installing a second stream never changes it; uninstalling the selected stream refuses until another is selected (--force overrides, logged). aslice versions php shows the matrix: installed streams, the pinned/default/session selections, and which extensions are installed per stream.

Extensions bind to exactly one runtime version. This is where version managers historically give up — pecl compiles against whichever phpize ran first, pip installs into whichever site-packages happens to be writable, and the result only looks shared until an ABI breaks. aslice splits the problem by who does the installing:

Tools ride the runtime. The third category is interpreter-target tools with no native code against the runtime ABI: composer (a phar), yarn, prettier, poetry. These are ordinary slices that declare [ride] runtime = "php": the slice installs once, its shim resolves the runtime through the normal session → project → default order at exec time, and the tool launches under that runtime — composer's PHP is always your selected PHP, switching automatically when you switch. This is Volta's best idea (a global yarn that follows your node) generalized to every ecosystem, with one deliberate difference: riding is a property of the tool's formula, never user configuration, because whether a tool can safely ride is a fact about its code, not a preference.

Upgrades stay in their lane. aslice upgrade php moves within the selected stream only — 8.4 patches, never 8.5; its output mentions newly available streams (php 8.5 is available: aslice install php@8.5) without touching them. That is the contract that makes aslice upgrade safe on a production Mac mini: the runtime your sites, services, and crontabs resolve to changes only when you say use, pin, or default. GC is conservative in the same direction: a stream that is selected as default, pinned by a project aslice.toml the DB knows about, or referenced by an enabled service is never collected.

Doctor. doctor.runtimes checks: the shim directory is present and precedes the profile on PATH (with the exact fix line); every declared shim resolves (no dangling selections — e.g. a default pointing at an uninstalled stream); every enabled service's versioned alias resolves to an installed stream; extension scan directories match the DB (a loader for an epoch whose runtime is gone); orphaned userbases reported, never removed. All read-only, same remedy-text standard as §12.6.


12.10 Trust store: modern CA certificates on a frozen platform

ca-certificates supplies a signed private PEM bundle for aslice userland. Shell integration points compatible clients at it. Updating certificates does not upgrade TLS implementations; --crypto separately updates aslice crypto-provider packages and never replaces SecureTransport.

ca-update --keychain uses a signed policy inventory and the protected helper, preserving supported purpose/domain constraints and refusing entries whose restrictions cannot be represented. PEM extraction alone does not reproduce Mozilla's browser trust policy. Roots and intermediates have distinct import operations; intermediates never become trust anchors merely because they were imported. --apple-certs uses the same constrained lifecycle for Apple's PKI, with service-specific validation and no promise to revive obsolete protocols.

Protected receipts distinguish existing Apple/user entries from aslice-owned additions. Updates preview and authorize retirement of aslice-owned roots; --keychain-remove reverses only unchanged owned effects. Known distrust is never silently undone by generation rollback. STATE-AND-RECOVERY §9 owns this lifecycle. doctor.truststore.* reports age, configuration, known retirement, and conflicts.

12.11 System patches: flagged, reversible replacement of Apple-provided files

[system-patch] declares exact Apple-provided tool/configuration/data paths, a mandatory reason, and security prerequisites. The kernel, dyld, libSystem, /System, platform-binary libraries, and early-boot consumers remain refused. The capability is available to official/local repositories and explicitly granted verified repositories; never to third-party repositories. Grafts cannot bypass it.

The helper keeps exact originals and recovery metadata in protected root-owned storage and installs verified replacements from protected closures. Writable paths use journaled file operations. Catalina System-volume changes use a Recovery-assisted offline backend; Big Sur/Monterey changes stage a complete patch set and select a boot snapshot. SIP, authenticated root, FileVault, and startup-security conditions are checked separately.

SYSTEM-VOLUMES specifies prepare, Recovery application, pending-reboot state, finalization, rollback, and OS-update handling for 10.11–12. A volume rollback can require Recovery and reboot; it is not an instant profile swap. Originals are versioned by OS baseline and are never restored over an intervening OS update. A missing backup or incompatible baseline stops the operation with a recovery remedy.

system-patch list, status, and restore remain the inspection/restoration commands; prepare and finalize manage protected-volume transitions. Every mutation requires explicit system-change consent. The client refuses an OS/hardware/security configuration before mutation until its adapter has passed the required patch/boot/restore drill.

12.12 Self-update: aslice is package zero

aslice is package zero, installed as an immutable artifact. A known-good supervisor launches the proposed replacement as a child, checks it against a versioned state snapshot, and retains responsibility through activation and post-activation health checks. These manager-integrity checks precede the durable commit; ordinary service readiness checks follow it. Before commit a failure reverses tentative activation; after commit restoration is a new transaction preserving history and trust floors. It never re-execs away its own recovery capability before success.

Database migration creates a new versioned copy; old manager/state pairs remain usable. A retained bootstrap recovery entry point handles power loss or supervisor failure. Shims are updated transactionally too. aslice recover runs recovery before permitting further mutations. STATE-AND-RECOVERY §6 defines crash cases and decommission.

self-update --check is read-only. Package holds and configured release environments apply; crossing a format major requires an explicit migration plan and confirmation. Signing/notarization precede freezing the served artifact inventory, and the authentic bootstrap verifies detached signatures.

12.13 Declarative system setup: aslice-machine.toml and the aslice machine commands

A Mac back from system recovery is blank, and the path from blank to ready to work is an afternoon of remembering: which packages, which Dock settings, which shell, which services, which runtime streams. aslice specifies one declarative file — aslice-machine.toml — and one command group: after the installer, aslice machine apply (the file defaults to ./aslice-machine.toml; an https:// URL works too) takes a blank machine to a working one, and because the file is plain TOML data it doubles as the shareable common language for "this is how my machine is set up". The inverse, aslice machine export, captures an existing machine back into the file. Full schema and semantics: SETUP.md; what follows is the architecture.

The file is a wishlist with preferences, not a lock. packages = ["extended:ffmpeg@7", "postgresql +ssl"] carries the same constraints install accepts; [runtimes.default] carries §12.9's stream selections; [services] carries the enable set; [defaults.user."<domain>"] / [defaults.system."<domain>"] carry macOS preference keys; [shell] carries the login shell; [aslice] and [[repos]] carry configuration and extra repositories. Like a Brewfile it is the human layer above exact state — the difference from a lock is the package.json/package-lock.json difference (PACKAGE-FORMAT §7). Unlike a Brewfile it is data, never code: no evaluation, no hooks, nothing executable — applying a stranger's file has a bounded, inspectable blast radius, and the plan shows every write before any happens (§12.2's plan/apply split, now pointed at whole machines).

One operation, three documents, two spellings. Converging a machine, replaying a lock, and executing a saved plan are the same operation at three fidelities — make reality match this document. The document kind is still detected by content (JSON plan, lock_version, schema), but the spelling is split: plans and lock files keep the top-level verb (aslice apply plan.json, aslice apply aslice.lock), while the machine file lives under the aslice machine group — aslice machine apply, defaulting to ./aslice-machine.toml. The split is deliberate: aslice-machine.toml is a reserved, self-describing name other tools can recognize, and a machine file passed to top-level apply (or a plan passed to machine apply) is refused with a pointer to the right spelling. A machine document is planned first: wishlist resolved against the current snapshot, preferences diffed, the full plan rendered and confirmed before execution. Ordering puts privileged work last (repositories → config → packages → selections → services → user defaults → system defaults + shell), so a plan refused at a consent gate still lands everything unprivileged and reports the remainder as skipped-refused.

Convergence, with discipline. Apply is idempotent — a second run is a no-op — and additive by default: it asserts, never removes what the file doesn't mention. --prune opts into retraction, bounded two ways: it touches only records the file itself applied, and it never removes repositories (trust decisions stay sticky, REPOSITORIES §10). Before every preference write, shell change, or /etc/shells enrollment, the pre-change value is recorded against the new generation — §12.11's backup discipline generalized — so aslice rollback restores preferences and login shell together with the profile, and aslice history attributes every applied change to its file hash and generation.

Two new consent-gated writes. System-domain preferences (/Library/Preferences) and enrolling a non-Apple login shell in /etc/shells are OS-territory writes, otherwise barred by N5 (§2.2). They join [system-patch]'s model exactly: declared in the file, executed by aslice-system (§10.4), interactively prompted with their targets named, refused non-interactively (exit 2) unless --accept-system-changes is passed — one flag, one contract across §12.7, §12.11, and here. Shell changes resolve through the profile path, never a raw store path, so generation rollback moves the login shell back automatically.

Export captures what is knowable. Packages, selections, services, shell, repositories, and non-default config are aslice's own state — exported completely, deterministically sorted for diffing. Preferences are not knowable: there is no baseline to diff a preferences folder against, and application domains can hold account tokens and machine-specific values, so export captures them only for domains named with --defaults / --system-defaults, emits deferred value types (dict, data, date) as comments, and stamps the output with a review-before-sharing warning. Locally-built packages and foreign (non-aslice) shells are commented out with notes rather than exported as if reproducible. aslice machine import --from-brewfile provides migration (SETUP §5): mechanical translation with a printed skip list for cask/mas/vscode.

Limits. Not a dotfiles manager (chezmoi/Stow/git own ~/); not a system imager (FileVault, SIP, accounts, panes without domains are untouched); not fleet configuration management (no agent, no drift loop — launchd running aslice machine apply is the entire enforcement story); not exact reproduction (that is the lock's job). Keychain items are never read or written.

12.14 Orchard maintenance: the maintainer's CLI

The per-formula verbs (create, lint, test, bump-pr — §12.1) and the publishing pipeline (aslice repo build/sign/publish — §9.6) bracket the maintainer's day; everything in between — keeping a whole orchard healthy — is the aslice orchard group. Every verb operates on a local orchard checkout: the optional [path] argument defaults to the current directory, discovered by walking up to the directory whose children are formula directories. The mutating verbs follow the bump-pr contract end to end: edit the formula, validate, lint, and open the PR — on a plain git remote without PR machinery, print the branch and the diff instead.

Lifecycle verbs — the [deprecation] table, edited by command. Schema in PACKAGE-FORMAT §3.14, policy in ORCHARD-POLICY §8:

aslice orchard deprecate extended:ffmpeg --reason upstream-eol --replacement extended:ffmpeg7 \
    --date 2027-03-01 --disable-date 2027-09-01
aslice orchard disable extended:ffmpeg        # pull disable_date forward to today
aslice orchard undeprecate extended:ffmpeg    # rescind — remove [deprecation]; the reason goes in the PR body
aslice orchard tombstone extended:ffmpeg      # remove the formula; the index tombstone is permanent (§3.14)
aslice orchard rename extended:ffmpeg extended:ffmpeg7 # deprecate-as-renamed + scaffold the successor formula

Each verb rewrites [deprecation] (or removes the formula) and validates what the linter will check anyway: dates in order, replacement present if and only if reason = "renamed", the replacement resolving in the orchard. tombstone refuses a name that still has enabled dependents, and ORCHARD-POLICY §8's security fast path is deprecate --reason security --disable-date <today> — a vote and a merge, not a special command.

Orchard-wide health. aslice doctor checks one machine (§12.6); aslice orchard doctor checks one orchard:

The merge gate, locally. aslice orchard ci [pkg…] [--flavors v2,v3] [--all] runs the six-gate check of §13.4 / ORCHARD-POLICY §10 on the maintainer's machine, before the farm has to: lint, a sandboxed build per declared flavor at the formula's min_os through the BUILD-INFRA harness (one harness, two scales — the laptop run and the farm run are the same pipeline), the tests.star smoke test, and the ABI gate diffed against a published index snapshot. The cross-OS smoke-run tier is honestly marked deferred-to-farm — a laptop cannot conjure the 10.11 VM — and so is the graft rehearsal gate (§12.15), which wants the same VMs: locally, a graft's manifest is lint-checked for completeness and the script hash-verified, and the observed-behavior diff runs on the maintainer's own OS release only. With no arguments, ci scopes to the packages changed against the upstream branch; --all prices the full orchard and says so before starting. The gate passing locally merges nothing — it makes the PR boring.

Impact queries. aslice orchard dependents x264 [--transitive] [--json] walks the orchard's dependency graph — built from the [depends], [extension], and [ride] declarations — and prints reverse dependencies, marking build/runtime, static, header, generated, and bundled edges separately from exact artifact bindings. All affected transitive consumers rebuild on changed inputs, even with compatible ABI. This is the query the farm's dependent-rebuild cascade (§13.4) runs server-side, and the query orchard ci runs to scope its ABI gate; giving it to the maintainer means "what breaks if I bump this" is answered before the PR, not after the merge.

The formula importer of §13.3 ships in this group as aslice orchard port --from-homebrew <formula> — named port, because §13.3 is honest that only the simple majority of Ruby formulae translate mechanically; the rest are ports, and the command says so. (aslice machine import --from-brewfile, §12.13, remains the user-side verb: migrating a machine, not authoring a formula.)


12.15 Vendor install scripts: grafts — declared, approved, monitored, reversible

A graft is a vendor installer script declared by path, hash, phase, and effective behavior manifest. Approval binds repository identity, version, script hashes, and manifest digest; expanded permissions invalidate it. Unsigned manifests require a fresh decision and cannot be allow-listed.

Supported scripts run only in an isolated staging view. A validated delta is committed through the helper journal; live root execution and unrestricted privileged IPC are not graft capabilities. Kexts and daemons are registered declaratively by the helper under the same repository gates as other mechanisms. Network-dependent inputs must be fetched and pinned before execution; irreversible remote or firmware effects are unsupported.

STATE-AND-RECOVERY §4 defines refusal conditions and rehearsal requirements. Each claimed OS must prove enforcement and crash recovery before graft admission. A behavior manifest or successful rehearsal alone does not establish containment or complete rollback.

13. Policies, Governance, and Migration

13.1 Package acceptance policy

13.2 Variant discipline

abi = true variants are governed by need, not quota. Each must name the exported interface it changes — the ABI scan supplies evidence, so the tag is checked, not the worthiness of the request — but there is no numeric cap: a package like ffmpeg legitimately carries many codec combinations, uncommon ones included, and which ones a user needs is the user's call, not the project's. Variants whose licenses permit compilation but not redistribution work like any other non-default variant: the client builds them locally from source and the farm never prebuilds them (§9.4), so the machinery is identical and nothing is forbidden. The discipline that survives the retired cap is where cost lands, not what users may want: the solver space stays tractable because the prebuilt matrix covers defaults plus demonstrated-demand variants (§9.4), and non-default variants build on demand. abi = false (build-flavor) variants are unconstrained — they cost the project nothing because they never spawn binary flavors.

13.3 Coexistence and migration from Homebrew

13.4 Governance


14. Roadmap

Phase 0 — Foundations (months 0–3, sequencing target rather than a delivery commitment) aslice-toolchain first: modern Clang/libc++ targeting darwin15, bootstrapped on the owned Mac Pro with Monterey as the proposed baseline, subject to toolchain validation, against the oldest archived SDK, then self-rebuilt. Then the minimal immutable store, generations, durable journal, retained recovery entry point, self-update supervisor, and persistent trust store (STATE-AND-RECOVERY §10). Then the C++ core skeleton: CLI, SQLite state, TUF client, zstd, Mach-O/otool wrappers — plus the build harness in local mode (aslice build with the full sandboxed phase pipeline, job/result schemas; BUILD-INFRA §12), because the core orchard seed is built with it. Bootstrap binary runs on every release 10.11–12 (VM-tested per release, including HFS+). Core orchard seeded with ~30 packages (curl, git, openssl, python, zstd, cmake, ninja) built on real hardware. Self-update ships in the first usable binary (§12.12) — retrofitting update mechanisms is how projects die — alongside the project hygiene documents (§13.4). The from-nothing sequence for the whole project — toolchain genesis, TUF root ceremony, orchard seed, farm standup — is executed and written down as docs/runbooks/GENESIS.md, so the platform can be stood up from nothing again and again.

Phase 1 — Usable (months 3–6) Full solver with variants; extend the Phase 0 store/profiles/generations; GHCR distribution; the build harness in both modes — aslice build locally and aslice farm coordinator/agents on real hardware, one pipeline (BUILD-INFRA.md); minisign slices; ~300-package core orchard, all flavors; adopt --from-homebrew; the freshness pipeline — [livecheck] in every core formula, scheduled orchard autobump opening bump PRs, and aslice livecheck/bump-pr for humans (PACKAGE-FORMAT §3.15; ORCHARD-POLICY §9); build farm Phase A + first self-hosted nodes. Repository client (repo add/list, TOFU key pinning) from the start — the canonical repository is the default transport, so the multi-repo machinery costs little extra. The ca-certificates slice and the private-bundle half of aslice ca-update (§12.10) ship here — every userland TLS fetch depends on them. So do the apple-roots slice and ca-update --crypto (§12.10): both are ordinary signed content and ordinary upgrades.

Phase 2 — Differentiated (months 6–12) ABI scanner with DWARF diffing; SBOM + audit; SLSA provenance; aslice-toolchain v2 (LLD-first linking, ccache integration); extended orchard to ~2,000 packages; popular-variant prebuilds chosen from community requests (§9.4); reproducible builds for core; vendor-binary packages (type = "binary", payload extraction, signer pinning), the Cask importer, and grafts — declared vendor install scripts with behavior manifests, the farm's rehearsal VMs, and the pnpm-style approval flow (§12.15), built on the proven aslice-system helper; aslice repo build/publish for third-party repositories; runtime version management — the shim layer, use/pin/default, riding tools, and version-bound extension slices with the farm's runtime-epoch build axis (§12.9); the opt-in System-keychain import halves of aslice ca-update (--keychain, --apple-certs, §12.10) and the [system-patch] category (§12.11), once aslice-system is proven in service and kext duty; declarative system setup — aslice-machine.toml and the aslice machine group (apply / export / import --from-brewfile, §12.13) — after the primitives it composes (services, runtime selections, repositories) are proven.

Phase 3 — Durable (year 2) Two-builder reproducibility cross-checks; transparency log; community mirror program; ~/Applications polish and GUI-app niceties; multi-user daemon if demand materializes; governance formalization.


15. Risks and Open Questions

Guided recovery inventories evidence automatically, resumes saved progress, and groups actual conflicts with recommendations. A known-good executable and recovery records outside the prefix support rebuilding beside the preserved original. Recovered selections do not authorize bytes: verify replacement artifacts and their closures independently. Reviewed salvage can leave isolated unaffected packages usable while normal activation or privileged integration remains blocked.

STATE-AND-RECOVERY §10.2 defines the recovery engineering contracts; its UX scorecard and acceptance scenarios are provisional targets and pending runtime work. Outcomes distinguish repaired, usable with listed unresolved repairs, and replacement prepared but activation blocked.

Risk Severity Mitigation
GitHub degrades self-hosted macOS runner support or GHCR terms change High Mirror-first index design (§9.1); Buildkite/Forgejo runner portability; static-mirror escape hatch means GHCR is replaceable
Apple removes Seatbelt in a future macOS Low for scope Target window is 10.11–12 — frozen releases where Seatbelt is present; the policy abstraction isolates the backend regardless
Modern Xcode can't target 10.11/10.12 (hosted floor ~10.13) Medium Self-hosted toolchain is Phase 0 and authoritative (§4.3); hosted CI is a bonus layer; archived SDKs cached on the farm; SDK use on builders stays within Apple's license on Apple hardware
Maintainer capacity High Frozen platform = fixed workload; automation-first orchard CI; small core orchard with quality bar; explicit scope refusal (no Apple Silicon, no new macOS)
Signing-key compromise Medium Offline root Pi, isolated networked release signer, encrypted backups, pre-launch recovery drills, explicit root-compromise rebootstrap, transparency log for detection
10.11-era testing hardware scarcity Medium Validate 10.11–12 guests in batches on capable owned Macs; missing required coverage blocks publication; Core 2 Duo testing is optional future coverage outside current inventory
ABI scanner false negatives (missed breakage) Medium Belt and suspenders: compat-version check + symbol fingerprint + reverse-dependency smoke tests in CI; when in doubt, rebuild dependents (we own the build farm)
C++ vulnerability in aslice itself Medium §5.3 program: subset, hardening, sanitizers, fuzzing, tiny trust-critical helpers
Vendor binaries are opaque — no source SBOM, no reproducibility, the binary itself is trusted Medium Signer + hash pinning (silent substitution hard-fails); payload-only SBOM with full file list; audit binds CVEs via CPE; core tier barred unless farm-hosted + payload-only (§13.1); users told what is and isn't verified (§10.7)
Vendor pulls or mutates a redistribute = false artifact Medium Hash pin fails rather than installing a different binary; the formula records last-known-good; community can negotiate hosting or archive a copy
A graft's behavior manifest understates the script, or an approved vendor script turns malicious Medium The manifest-derived Seatbelt profile denies undeclared writes and network by construction; farm rehearsal diffs observed behavior and a mismatch never ships; every captured write rolls back with its generation; unsigned manifests get the big fat warning and per-decision consent, never persisted (§12.15)
A [system-patch] replacement breaks software that expected the Apple original High Strictest gate in the system (official/local, verified only with an explicit per-repo grant, per-decision consent); lint-time refused-path list blocks catastrophic and platform-binary-library targets; exact backup + byte-verified restore; the §10.7 honesty rules cap what is claimed (§12.11)
A macOS update restores or upgrades an Apple file a [system-patch] has replaced Medium doctor.systempatch drift detection with explicit reapply/restore remedies, never silent re-patching; patches use protected closures and platform-specific journaled restoration; reboot verification may remain pending (§12.11)
GPL/license compliance for hosted binaries Low Corresponding-source archive mirrored per license; SPDX SBOMs make compliance auditable; redistribute = false covers software the project does not rehost
Community adoption never materializes Existential Scope stays hobbyist-sustainable by design; worst case, the core orchard remains a maintained artifact for the installed base

Open questions for early reviewers:

  1. ~~Default prefix~~ — resolved (v1.8): /opt/aslice by default, ~/.aslice as the no-admin fallback. The installer prefers the shared prefix; when it cannot create it, it offers a fully supported per-user install under ~/.aslice with identical semantics (§8.1, §10.3) — two documented layouts, not an arbitrary-prefix free-for-all. The name itself is settled: aslice.
  2. ~~Starlark vs. a stricter pure-TOML-with-templates build DSL~~ — resolved (v1.8): Starlark stays. Real legacy codebases need real conditionals and loops; the hermetic subset (no network, no filesystem outside the sandbox) carries the safety story, and escape hatches in a template language would be worse than a real one.
  3. ~~Whether local flag builds share store paths~~ — resolved by the state-and-recovery contract: compatibility keys may match; byte-distinct artifacts have distinct immutable addresses. Provenance does not replace artifact identity (STATE-AND-RECOVERY §1).
  4. ~~Telemetry~~ — resolved (v0.3, sharpened v0.4): aslice collects no telemetry or analytics of any kind, ever. No install IDs, no opt-in counters, no phone-home, no crash reporting — and no download-count-driven prioritization either, because volume mismeasures value on a platform where the rarest dependency may be the most irreplaceable (§9.4). The project is infrastructure, not a product, and its users — many on air-gapped audio rigs and lab machines — owe it no data. This is a charter-level commitment, not a tunable.
  5. ~~Whether the project's canonical repository should host any redistribute = false formulae in core~~ — resolved (v1.8): the current allowance stands. The canonical orchard hosts pointer-only formulae; core tier continues to require redistribute = true (§13.1, ORCHARD-POLICY §12) — core never depends on a vendor's server being up.
  6. ~~Whether verified repositories should install binaries immediately at enable time~~ — resolved (v1.8): enable implies binaries. The countersignature ceremony — key pinning, the trust prompt — is the consent; a second click is ceremony without security content (REPOSITORIES §9).
  7. ~~Whether the system capability (§12.7) should ride on the verified trust level or be a separate per-repo grant~~ — resolved (v1.8): it rides on verified. The countersignature vets the maintainers, and the §12.7 flow — per-decision warnings, no "always allow" — is where the ceremony lives. (Contrast #10, one severity level higher, where the answer went the other way.)
  8. ~~Service health-check failure behavior~~ — resolved (v1.4): ask the user. A failed post-upgrade health check (§12.8, step 5) prompts interactively to roll the generation back and restart the previous version; the default is stay-and-inspect, non-interactive runs fail loudly without rolling back, and --rollback-on-service-failure is the explicit unattended path. Silent automatic rollback was rejected: it can mask a good new version behind a transient port conflict, and a package manager that destroys evidence of a failure is harder to trust than one that asks.
  9. ~~Project-local dependency directories~~ — resolved (v1.8): out of scope, permanently. .venv, vendor/, node_modules remain the ecosystem tools' job; the shim layer binds runtimes and their extensions and stops there. Reimplementing pip/poetry/composer is a non-goal — and the thin convenience layer is how the creep would start.
  10. ~~Whether verified repositories should ever serve [system-patch] packages~~ — resolved (v1.8): yes, under an explicit per-repo grant. aslice repo allow-system-patch <name> — refused by default, recorded in the state DB, revocable — is the ceremony that is security at this severity: the countersignature vets a repository to distribute software, and rewriting the OS warrants one deliberate decision from the machine's owner beyond that vetting. Third-party repositories: never (§12.11; REPOSITORIES §3).

Appendix A. Specification status

Artifact identity, protected execution, exact replay, and transaction recovery are specified in STATE-AND-RECOVERY. Protected-volume activation and restoration are specified in SYSTEM-VOLUMES. These contracts and their structural fixtures do not establish runtime correctness. Each feature remains gated on its named implementation, security, and platform acceptance tests.


Appendix B. References

History

Document revision history | Version | Date | Changes | |---|---|---| | v1.35 | September 2026 | Classify FFmpeg as extended and align affected package references and examples. | | v1.34 | September 2026 | Replace inline navigation with a collapsible Contents list; preserve section labels, links, and order. | | v1.33 | September 2026 | Replace the example command list in §12.1 with a purpose-grouped public command index linked to owning man pages; complete family coverage and distinguish unspecified interfaces from implemented behavior. No command contracts changed. | | v1.32 | September 2026 | Specify dependency-driven security remediation, explicit update and origin decisions, and the applicable farm, maintenance, and evidence contracts. Supersedes ABI-only rebuild and cost-first selection policies where previously stated; runtime and measured acceptance remain pending. | | v1.31 | September 2026 | Describe aslice mechanisms without competitive rankings; align artifact bindings, protected restoration, and self-update commit boundaries, and retarget specification citations. | | v1.30 | September 2026 | Align guided recovery, mixed-orchard transactions, explicit waiting, post-commit service checks, trusted rebuilding, and unaffected runtime access; link pending engineering decisions and acceptance targets. | | v1.29 | September 2026 | Integrate six-role SQLite storage, complete solve-cache keys, durable records, and database maintenance commands from DATABASE; runtime implementation remains pending. | | v1.28 | September 2026 | Documentation audit repairs: contract summaries aligned; owner-approved namespace, rollback, GC, naming, prefix, and graft decisions applied where relevant; semantic anchors and explicit citations added. Runtime implementation and platform acceptance remain pending. | | v1.27 | September 2026 | Consolidate revision notes into a collapsible history table; no specification changes. | | v1.26 | September 2026 | align user-flag and universal-vendor summaries with STATE-AND-RECOVERY §1–§2: conditional ABI/CPU substitution, distinct artifact identity, and the i386 ceiling based on required execution. | | v1.24 | September 2026 | owner merge becomes the final human release approval, with automatic signing on a dedicated networked Pi and serialized atomic publication. Automatic targets/snapshot renewal replaces manual renewal; the root remains offline. The manual-release design above is superseded. Services and acceptance drills remain implementation work (KEY-RUNBOOK §2.1, §7); schemas and client signature formats are unchanged. | | v1.23 | September 2026 | **Superseded design record:** initial signing uses a single owner, separate offline root and release Pis, encrypted backups, and manual release batches. The Mac Pro VM prepares and publishes; multi-party custody and hardware tokens are deferred. KEY-RUNBOOK defines renewal, rotation, recovery, and pre-launch drills; prior custody requirements are superseded. | | v1.22 | Not recorded | describes the two owned Macs, capability-based scheduling, pending coverage and rebuild gates, the shared-host signing VM boundary, and the proposed Monterey bootstrap baseline (§4.3, §9.3, §14, §15); no CLI or schema change. | | v1.21 | Not recorded | adds [TOOLCHAIN.md](TOOLCHAIN.md) as the authoritative toolchain document; §4.3 keeps the rationale and points at it — no design change. | | v1.20 | Not recorded | fills §12.1's gaps, found by the full-corpus review: `aslice plan` and `aslice lock export` (the plan/apply split, §12.2, §12.13, PACKAGE-FORMAT §7), `aslice mark` (§8.4), `aslice profile prefer` (§8.2), `aslice db query` (REPOSITORIES §11), and `aslice help` (MANUAL §14, man/) join the canonical command list; no design change — every verb was already specified where cited). | | v1.19 | Not recorded | opens **vendor install scripts** — the payload-only rule gains its declared exception: a vendor script is a **graft**, declared in the formula with a **behavior manifest**, approved pnpm-style (interactive prompt recorded in the state DB, allow-list declarable in `aslice-machine.toml`, `--accept-grafts` for unattended runs), sandboxed to its manifest while it runs, captured for generation rollback, and **rehearsed, verified, and signed by the farm** for core and extended — unsigned manifests carry the big fat warning and per-decision consent (§2.1 G9, §2.2 N5, §3, §10.1, §10.4, §10.5, §10.7, §12.1, §12.3, §12.4, §12.7, §13.1, §13.4, §14, §15, new §12.15; schema in PACKAGE-FORMAT v0.15 §3.11; policy in ORCHARD-POLICY §12; trust levels in REPOSITORIES §3; owner decision, September 2026. | | v1.18 | Not recorded | renames the declarative-setup file and splits its command surface by document kind: `setup.toml` becomes `aslice-machine.toml` — a reserved, self-describing name that other tools can recognize — and its verbs move under the `aslice machine` group (`aslice machine apply` / `export` / `import --from-brewfile`), while top-level `aslice apply` keeps saved plans and lock files only, refusing a machine file with a pointer to the right spelling (§12.13; schema in SETUP.md v0.9; owner decision, September 2026). | | v1.17 | Not recorded | retires the `abi = true` variant cap: variants are governed by need and honest ABI tags rather than quota — ffmpeg-class packages carry as many codec combinations as users require, including compile-only licenses served as non-default local builds (§13.2, §7.5); the vendor-binary distribution modes lose their license-gate framing, keeping the mechanics and dropping the policing (§9.5, §12.4, §13.1, §15). | | v1.16 | Not recorded | adds the **orchard-maintenance command group** — `aslice orchard` gains the lifecycle verbs (deprecate / disable / undeprecate / tombstone / rename — the `[deprecation]` table edited by command, validated, linted, and shipped as a PR), orchard-wide health (`lint`, `doctor`, `freshness` — the last computing the days-behind-upstream number ORCHARD-POLICY §9 publishes), the local merge gate (`orchard ci` — §13.4's five gates on the maintainer's machine through the same BUILD-INFRA harness the farm runs), and reverse-dependency impact queries (`orchard dependents` — the ABI blast radius before the PR, not after the merge); §12.1, new §12.14. The §13.3 Homebrew-formula importer is named: `aslice orchard port --from-homebrew`. | | v1.15 | Not recorded | adds a NOMENCLATURE.md vocabulary reference to the header; no design, schema, or factual changes. | | v1.14 | Not recorded | is a prose polish in the project's technical-writing voice — six sentences reworded for precision (§1.1, §4.1, §5.3, §7.5, §9.3, §11); no design, schema, or factual changes. | | v1.13 | Not recorded | is a second editorial pass — sentence-level revision for readability; no design, schema, or factual changes. | | v1.12 | Not recorded | is an editorial pass — prose revised for directness throughout; no design, schema, or factual changes. | | v1.11 | Not recorded | adds **declarative whole-machine setup** — `setup.toml`, the unified `aslice apply` verb (plan / lock / setup documents), `aslice export`, and `aslice import --from-brewfile` (§12.13; schema and semantics in [SETUP.md](SETUP.md)); N5's exception list grows to include consent-gated system-preference writes and `/etc/shells` enrollment (§2.2); supersedes the `aslice bundle` wishlist proposal. | | v1.10 | Not recorded | corrects the GitHub-hosted-CI facts: the `macos-13` Intel image was retired in December 2025, not autumn 2027 — the last hosted Intel label is `macos-15-intel`, available until August 2027 (§1, §9.2, §13 Phase A); adds the per-profile `aslice link`/`unlink` commands for `link = false` shadowing packages (§12.1; PACKAGE-FORMAT §3.8); and records the owner decision that unsigned vendor artifacts may ship in the extended orchard with `signer` omitted — announced loudly at every install — while the core stays signed-only (§12.2, §12.4; schema in PACKAGE-FORMAT §3.11, policy in ORCHARD-POLICY §12). | | v1.9 | Not recorded | runs the **genesis audit** — a project-wide review of from-nothing bootstrap assumptions: the installer gains a loudly-printed plain-HTTP fallback for TLS-dead machines, fetching the same hash-pinned artifacts (§10.3); the repository tree's vendored-source role is made explicit — every source artifact the farm fetches lives in `blobs/sha256/`, and the client fetch order is upstream → formula `mirrors` → the repository's own blob area, all three under the same pinned sha256 (§9.6; machinery in BUILD-INFRA v0.6, the mirror story in REPOSITORIES v0.8, dead-upstream policy in ORCHARD-POLICY v0.8); the project-hygiene bullet records what actually shipped in Phase 0 — `SECURITY.md`, `CONTRIBUTING.md`, `docs/KEY-RUNBOOK.md`, and deliberately no `CODE_OF_CONDUCT.md` (§13.4); and the from-nothing sequence for the whole project is codified as `docs/GENESIS.md` (§14). | | v1.8 | Not recorded | lands the review's remaining operational machinery — **self-update** (aslice is package zero: TUF-verified slice, generation swap, supervised activation with retained recovery entry point, automatic rollback — §5.2, §12.12), the **`on_request` install record** and **`aslice clean`** cache eviction (§8.4), the full day-two CLI surface (`outdated`, `reinstall`, package-hold `pin`/`unpin`, `livecheck`, `test`, `create`, `bump-pr`, `exec`, `shellenv`, `store verify` — §12.1), the **system-framework allowlist** (§13.1), and the **merge gates and project-hygiene documents** (§13.4) — and resolves the eight remaining open questions (§15): the prefix gains a per-user `~/.aslice` fallback (§8.1, §10.3), Starlark stays, local `abi = false` builds share store paths with DB-recorded provenance (§5.2), pointer-only formulae keep their current allowance, verified repositories keep enable-implies-binaries and the `system` capability, project-local dependency directories stay out of scope, and `verified` repositories may serve `[system-patch]` under an explicit per-repo grant (§12.11). | | v1.7 | Not recorded | extends trust-store management and amends a founding line: `aslice ca-update --crypto` upgrades the crypto-provider slices (modern ciphers/TLS for userland, with SecureTransport's limits printed, never hidden), `--apple-certs` imports Apple's own roots — which Mozilla's program does not carry — from a pinned `apple-roots` slice into the System keychain; and the absolute "never touch the system" stance becomes a declared exception: **`[system-patch]` packages** (§12.11) may replace Apple-provided files through a backup + profile-symlink + generation-rollback mechanism — consent at every decision point, official/local repositories only, refused paths blocked by construction (§2.2 N5, §10.4, §10.7, §12.10, §12.11, §13.1). | | v1.6 | Not recorded | adds **trust-store management** — `aslice ca-update`: the CA bundle becomes an ordinary signed, generation-managed slice (`ca-certificates`), its source configurable with the Mozilla root program (via curl's caextract) as the default; profile env wiring heals userland TLS (curl, git, python) completely; and an opt-in `aslice ca-update --keychain` imports the missing modern roots into the **System keychain** through `aslice-system` — machine-wide healing for Safari, Mail, and every SecureTransport app, recorded to the certificate and reversible to the certificate (§4.1, §12.6, §12.10). | | v1.5 | Not recorded | adds **first-class multi-version runtime management** — the Volta lesson, taken seriously: version switching is the package manager's job, not a second tool's. A shim layer multiplexes versioned runtimes (php, nodejs, ruby, python, …) by session (`aslice use`), project (`aslice pin`), and default (`aslice default`) selections; interpreter-target tools like composer and yarn **ride** the selected runtime; and compiled extensions are slices bound to the runtime's ABI epoch, so an extension is always installed for exactly one runtime version — as are pip/gem/npm/pecl installs, through per-version userbases (§8.5, §12.9; schema in PACKAGE-FORMAT v0.5 §3.13). | | v1.4 | Not recorded | resolves open question #8: a failed post-upgrade service health check **asks the user whether to roll back** — an interactive prompt (default: stay and inspect) that swaps the generation back and restarts the previous service version on assent; non-interactive contexts never prompt and never auto-rollback, with `--rollback-on-service-failure` as the explicit unattended path (§12.1, §12.8). | | v1.3 | Not recorded | adds **launchd-native service management**: packages describe services declaratively in the manifest (`[service]`, PACKAGE-FORMAT v0.4 §3.8), `aslice service` provides status/start/stop/restart over real launchd jobs, and upgrades orchestrate stop → atomic swap → restart so a running service is never updated out from under itself (§8.3, §10.4, §12.1, §12.8). Root-domain daemons go through `aslice-system` and the repository `system` capability; user agents stay unprivileged and ungated. | | v1.2 | Not recorded | opens a narrowly-scoped **system-software category**: kernel extensions and SIP-disabled development software become installable as declared `[system]` packages — warned at every decision point, elevated per-operation by a dedicated helper, gated by repository trust level — replacing the blanket rejection with an honest, reversible install path (§10.4, §10.7, §12.1, §12.7, §13.1). | | v1.1 | Not recorded | specifies **`aslice doctor`**: a read-only, scriptable sanity battery — machine, store, profiles, database, repositories, coexistence, environment — where every fail names its remedy and `--fix` is narrow and loud (§12.6). | | v1.0 | Not recorded | adds the **logging design**: structured, local-only operation logs with a message-quality standard — every error is actionable, security events are unsuppressible, and nothing ever leaves the machine (§5.2, §8.1, §12.1, §12.5). | | v0.9 | Not recorded | adds **cross-repository overlap resolution**: ambiguous bare package names prompt the user, the decision is remembered in the SQLite state database, revalidated on repo changes, and scriptable via `aslice repo prefer` (§9.6, §12.1; REPOSITORIES.md §10–§11). | | v0.8 | Not recorded | completes the repository story: a **shipped official source list**, **inherent trust levels** (official / verified / third-party / local — enforced capabilities, not labels), and **dual signature schemes** — Ed25519/minisign canonical, OpenPGP (GPG) built-in first-class for third-party ecosystems (§9.6, §10.2; full spec in [REPOSITORIES.md](REPOSITORIES.md)). | | v0.7 | Not recorded | adds the **build-infrastructure design**: one harness — `aslice build` on a user's machine, `aslice farm` on the farm — running the identical sandboxed pipeline at both scales (§5.1, §9.3; full spec in [BUILD-INFRA.md](BUILD-INFRA.md)). | | v0.6 | Not recorded | opens **32-bit vendor binaries**: i386 and universal pkg/dmg payloads install on the releases that still execute 32-bit code (10.11–10.14) — distributed, never built (§2.2 N6, §12.4; PACKAGE-FORMAT v0.3). | | v0.5 | Not recorded | adds the **repository system** (§9.6) and **vendor binary packages**: software that only ships as a `.pkg`/`.dmg`, hosted or vendor-fetched, installed without ever running installer scripts (§12.4; schema in PACKAGE-FORMAT v0.2 §3.11). | | v0.4 | Not recorded | sharpens it: **download counts are rejected as a value signal too** — on a deprecated-OS platform, obscure ≠ low-value (§9.4). | | v0.3 | Not recorded | resolves open question #4: **aslice collects no telemetry or analytics of any kind, ever** — the project is infrastructure, not a product (§2.2 N7, §9.4, §15). | | v0.2 | Not recorded | extends the platform floor from 10.15 (Catalina) to 10.11 (El Capitan) — see §4 for the consequences (three flavors, self-hosted toolchain in Phase 0, HFS+ support). | | Not recorded | September 2026 | corpus review corrections: artifact identity, protected execution, durable recovery, trust persistence, replay, platform limits, and examples aligned with STATE-AND-RECOVERY and SYSTEM-VOLUMES. These are specification changes; runtime acceptance remains pending. |

Source document