English · 简体中文
Andromeda is an AI-native desktop operating system project for PC and Mac hardware.
It aims to keep the gaming, Office, and file compatibility people rely on from Windows, adopt the reliability and hardware/software experience of macOS, and turn Codex/Claude Code-style task execution, authorization, verification, and recovery into OS infrastructure — rather than bolting yet another chat box onto a desktop.
The central claim is a single sentence: an AI may complete tasks for the user, but the model itself never holds system authority. Models propose plans; deterministic code owns authorization, state transitions, execution, verification, and persistence.
- Current status
- First-principles goals
- Architecture
- Repository layout
- Quick start
- Core model
- CLI reference
- HTTP API reference
- Durability and concurrency
- Building the installable OS image
- Hardware compatibility and support tiers
- Security boundary
- CI and merge gates
- Roadmap
- Documentation index
- Contributing
- License
The project has reached its first installable Daily Driver Candidate (virtual hardware):
- an x86-64 UEFI offline installation ISO based on Fedora bootc 44 and KDE Plasma;
- automated QEMU/OVMF acceptance covering installation onto a blank disk from the ISO and first boot after the ISO is removed;
- lifecycle acceptance covering a bootc update to revision 2, reboot verification, and rollback to revision 1;
- Flatpak/Discover, LibreOffice, Firefox, Chinese input, audio/video, printing/scanning, firmware, gaming fundamentals, and common file-format support;
- automated acceptance of a real Plasma Wayland session, PipeWire, Office formats, browser launch, the security exposure surface, and user-data persistence across update and rollback;
- a buildable, testable Rust workspace (
unsafe_code = "forbid"workspace-wide); - task, risk, capability, isolation, and state-machine contracts the model cannot bypass;
- a durable task service, HTTP API, and developer CLI;
- Linux, macOS, and Windows hardware probing with Hardware Compatibility Manifest (HCM) matching;
- boot-time driver diagnosis, HCM v2 artifact/evidence gating, and a virtual hardware matrix covering NVMe/SATA/IDE, e1000e/e1000, XHCI/UHCI, and HDA/AC97;
- cross-platform CI, a product plan, and topic research.
This milestone proves Andromeda can build, install, reach a real desktop, run a set of day-to-day workflows plus the AI task control plane, and complete update and rollback.
Important
It is still not a certified consumer operating system. Only QEMU/KVM x86-64 + OVMF meets the automated acceptance bar, and no consumer PC or Mac is claimed to have reached Supported or Certified.
Installable OS acceptance #30341131852 (2026-07-28)
passed end to end on a fresh 32 GiB virtual disk. The tested ISO has SHA-256
c04d8f6de780f978e261e1867283894abf2a7996b6105525660c52343ae45073.
That record used a 32 GiB disk; the current os/scripts/test-install.sh has since moved to
64 GiB, so the evidence snapshot predates that change.
GCP nested-KVM daily-driver run (2026-07-28)
completed offline installation, Plasma Wayland first boot, the revision 2 update, and the
revision 1 rollback, also on a fresh 32 GiB virtual disk. The tested 3.8 GiB ISO has SHA-256
6f8d74e5f14b7dab9c478b8fd538defbdbde717dee62bbc3c7ca5c13cc597108.
Note: that PASS predates a fix to the evidence extractor, and the fix has only been re-verified
offline against the original serial logs; a full GCP re-run that goes green in one pass with
the final scripts is still outstanding.
Per-item acceptance results and artifact identities are in the Developer Preview installation guide and Daily Driver Candidate E2E.
- Keep running the games, Office workflows, and important files users already have.
- Stay bootable, explainable, and recoverable when updates, drivers, and applications fail.
- Let AI complete tasks for the user without the model itself holding system authority.
- Probe broad hardware, but promise support only for hardware that passes real testing.
- Make Windows/macOS migration a product flow with manifests, verification, and fallback.
flowchart TD
U["User / Task Center"] --> I["Intent and plan"]
I --> P["Deterministic policy"]
P --> C["Capability Broker"]
C --> R["Durable task runtime"]
R --> E["Attested executor<br/>sandbox / microVM / host broker"]
E --> V["Verifier and evidence"]
V --> A["Audit / rollback / compensation"]
H["Hardware probe + signed HCM"] --> E
The current installation image runs the control plane, policy evaluation, and hardware preflight. The real executor, credential broker, and full OS transaction backend in the diagram remain later security milestones. The task API does not execute actions proposed by a model.
Implementation boundary of the data flow:
Intent
-> versioned ActionPlan ✅ implemented
-> schema / risk / DAG validation ✅ implemented
-> Capability + deterministic policy ✅ implemented
-> durable TaskRecord ✅ implemented
-> attested Executor ❌ not implemented
-> independent Verifier ❌ not implemented
-> Evidence + audit + recovery ⚠️ data model exists, executor does not
| Crate | Responsibility |
|---|---|
andromeda-core |
Intent, ActionPlan, L0–L3 risk, Capability, Evidence, recovery semantics, task state machine |
andromeda-policy |
Deny-first deterministic authorization, capability scope, expiry, isolation levels, external side-effect confirmation |
andromeda-runtime |
Atomic JSON persistence, cross-process locking, DAG validation, event history, optimistic concurrency |
andromeda-taskd |
Task HTTP control plane, loopback-only by default |
andromeda-cli |
Task creation/inspection/policy evaluation/state transitions and hardware probing |
andromeda-hardware |
Privacy-conscious Linux/macOS/Windows probing and HCM evaluation |
.
├── crates/ # Rust workspace (6 crates, unsafe forbidden)
│ ├── andromeda-core/ # Task, plan, capability, risk, and state-machine contracts
│ ├── andromeda-policy/ # Deny-first deterministic authorization engine
│ ├── andromeda-runtime/ # Atomic persistence, cross-process locking, TaskService
│ ├── andromeda-taskd/ # Loopback-only HTTP control plane
│ ├── andromeda-cli/ # The `andromeda` developer CLI
│ └── andromeda-hardware/ # Cross-platform probing, driver diagnosis, HCM matching
├── os/ # Fedora bootc 44 + KDE Plasma installable image
│ ├── Containerfile # bootc base image definition
│ ├── files/ # systemd / bootc / libexec files injected into the image
│ ├── installer/ # Kickstart, preflight, platform guard
│ └── scripts/ # build-iso / test-install / hardware matrix / GCP E2E
├── schemas/ # hardware-compatibility-manifest.schema.json
├── examples/hcm/ # Example Hardware Compatibility Manifest
├── skills/ # In-repo engineering skill (boundaries, gates, failure defenses)
├── docs/ # Research, product plan, development guides, ADRs, reviews
└── .github/workflows/ # ci.yml (three platforms) / os-e2e.yml (install acceptance)
- Rust 1.85 or newer (
rust-toolchain.tomlpins 1.85.0 with clippy and rustfmt); - Git;
- Linux, macOS, or Windows (all three are verified in CI).
Building the OS image has additional requirements — see Building the installable OS image.
cargo fmt --all -- --checkcargo test --workspace --lockedcargo clippy --workspace --all-targets --locked -- -D warningsThe report collects no serial numbers and is never uploaded automatically:
cargo run --locked --bin andromeda -- hardware probeReal output on an Apple silicon Mac:
{
"schema_version": 1,
"collected_at": "2026-08-01T13:33:09.645635Z",
"os_family": "macos",
"identity": {
"manufacturer": "Apple Inc.",
"model": "Mac16,7",
"board": null,
"firmware_version": null
},
"cpu": { "architecture": "aarch64", "model": "Apple M4 Pro", "logical_cores": 14 },
"memory": { "bytes": 25769803776 },
"boot": { "uefi": false, "secure_boot": null, "tpm2": null, "virtualization": true },
"devices": [],
"warnings": [
"macOS does not expose Apple boot policy as a TPM/Secure Boot equivalent; HCM must verify the platform-specific boot provider.",
"Detailed Mac device support requires an exact Asahi or Intel/T2 model manifest."
]
}Every field under boot is tri-state: null means could not be verified (for example, a
probe running without the privileges the platform requires), which is deliberately a different
conclusion from false (verified absent).
Driver binding and support-relevant device readiness:
cargo run --locked --bin andromeda -- hardware diagnosecargo run --locked --bin andromeda -- hardware check examples/hcm/developer-x86_64-pc.jsonReal result from the same Mac against the generic x86-64 PC manifest — probing successfully is not the same as being supported:
{
"manifest_id": "developer-x86-64-pc",
"selector_matched": false,
"requirements_met": false,
"declared_tier": "community",
"effective_tier": "blocked",
"boot_provider": "pc_uefi_shim",
"evidence": [
"HCM schema version 2 is current",
"memory 25769803776 >= required 8589934592",
"virtualization = true"
],
"missing": [
"hardware identity did not match any manifest selector",
"UEFI = false, expected true"
]
}effective_tier equals the manifest's declared tier only when the selectors and the
requirements are all satisfied; otherwise it drops to blocked (process exit code 2).
cargo run --locked --bin andromeda -- \
--state-dir .andromeda/state \
task create-inspection . --requested-by local-userThe command canonicalizes the target directory, mints a read-only file capability, creates one L1 inspection action, validates the schema, risk floors, dependency DAG, and capability coverage, then atomically writes the task record. It does not traverse or modify the directory — the current runtime contains no tool executor.
Result (path shortened for readability):
{
"plan": {
"schema_version": 1,
"task_id": "7979a783-3202-4c37-81e0-5504bb5f6049",
"intent": {
"summary": "Inspect /home/you/project",
"requested_by": "local-user",
"created_at": "2026-08-01T13:32:57.151433Z"
},
"actions": [
{
"id": "145a9da0-ec21-4004-b6dd-119605168b44",
"name": "Inspect directory metadata",
"kind": "inspect",
"target": "/home/you/project",
"required_capabilities": ["7b7b0aad-3e5a-485a-84f9-8b368085b430"],
"risk": "l1_sandboxed",
"recovery": "none"
}
]
},
"state": "ready",
"revision": 0,
"capabilities": [
{
"id": "7b7b0aad-3e5a-485a-84f9-8b368085b430",
"resource": { "type": "files", "root": "/home/you/project", "access": "read" },
"issued_to": "7979a783-3202-4c37-81e0-5504bb5f6049",
"expires_at": null,
"single_use": false
}
],
"events": [{ "actor": "local-user", "kind": { "type": "created" } }]
}cargo run --locked --bin andromeda -- \
--state-dir .andromeda/state \
task evaluate 7979a783-3202-4c37-81e0-5504bb5f6049{
"task_id": "7979a783-3202-4c37-81e0-5504bb5f6049",
"revision": 1,
"decisions": {
"145a9da0-ec21-4004-b6dd-119605168b44": {
"effect": "allow",
"reasons": ["risk, isolation, capability, and deny-rule checks passed"]
}
}
}RUST_LOG=info cargo run --locked --bin andromeda-taskdcurl -H "Authorization: Bearer $(cat .andromeda/taskd-token)" http://127.0.0.1:7777/healthzIt listens on 127.0.0.1:7777 with state directory .andromeda/state by default, and
generates a 0600 API token at .andromeda/taskd-token on first start.
Every request needs that token, /healthz included; there is no way to turn it off.
The token only distinguishes "this host's service account or root" from other local users —
there is still no remote authentication and no user identity, so it must not be rebound off
loopback.
A model may declare an action more dangerous than required, but never safer — the
ActionKind sets a risk floor that cannot be lowered.
| Level | Meaning | Minimum isolation |
|---|---|---|
L0Reasoning |
Reasoning with no tools and no external data | None |
L1Sandboxed |
Ordinary file/task operations | Sandbox |
L2StrongIsolation |
Unknown content, parsers, executable input | MicroVm |
L3ExternalSideEffect |
Network, system settings, real external side effects | Brokered + final confirmation |
ActionKind |
Minimum risk | Meaning of target |
|---|---|---|
reason |
L0 | — |
inspect / read_file |
L1 | Absolute path |
write_file / create_directory / delete_file |
L1 | Absolute path |
move_file |
L1 | Source path; destination lives in arguments.destination, and both endpoints need write coverage |
parse_untrusted_content |
L2 | Absolute path |
network_request |
L3 | host or host:port |
system_change |
L3 | System setting key |
external_call |
L3 | service:operation, split at the first : |
A capability is a resource-scoped permission that expires independently and never stores a secret value:
| Resource | Coverage |
|---|---|
files |
Paths under root after lexical normalization, plus read/write/read_write |
network |
An exact host (case-insensitive, trailing dot ignored); subdomains are never covered; a null port covers any port |
system_setting |
One named system setting key |
external_service |
One operation on one external service |
For each action, PolicyEngine checks, in order: the risk floor → whether isolation is
satisfied → deny rules → whether capabilities are present, active, cover the target, and are
bound to the subject. Any failed check yields deny, with every failure reason accumulated
into reasons for auditability. Only a missing L3 final confirmation yields ask. Everything
passing yields allow.
The default PolicySet denies /boot, /etc, /usr, /System, and C:\Windows, and
requires confirmation for external side effects. Paths are lexically normalized before
comparison, so traversal such as /tmp/../etc/passwd cannot dodge a denied root; targets that
cannot be normalized (relative paths, .. past the root) are denied outright.
stateDiagram-v2
[*] --> Draft
Draft --> AwaitingApproval
Draft --> Ready
Draft --> Cancelled
AwaitingApproval --> Ready
AwaitingApproval --> Cancelled
Ready --> Running
Ready --> Cancelled
Running --> Verifying
Running --> Failed
Running --> Cancelling
Verifying --> Succeeded
Verifying --> Failed
Verifying --> Cancelling
Cancelling --> Cancelled
Cancelling --> Compensating
Failed --> Compensating
Compensating --> Compensated
Compensating --> Failed
Succeeded --> [*]
Cancelled --> [*]
Compensated --> [*]
Key invariants:
Runningcannot jump straight toSucceeded— it must pass throughVerifying;Failedis terminal but keeps one outgoing edge,Failed → Compensating, so recovery semantics can reopen it;- two authorization-sensitive edges are re-checked against policy:
AwaitingApproval → Readyrequires a fully granted plan, so a task suspended for missing authorization must first receive the missing capabilities;Ready → Runningre-runs the policy engine at each action's per-action minimum isolation and refuses the transition if any action isdenyorask(a capability that expired afterReady, for example, is caught here).
Warning
Ready does not mean "authorized to execute". It only guarantees that policy would allow
the plan under the most permissive execution assumption. It does not guarantee that isolation
is sufficient at execution time, that capabilities are still unexpired, that an L3 action has
human confirmation, or that a capability came from a trusted issuer — the v0 control plane
does not validate an issuance chain, so a creator can mint its own capabilities.
Global flag: --state-dir <PATH> (env ANDROMEDA_STATE_DIR, default .andromeda/state).
| Command | Purpose |
|---|---|
andromeda task create-inspection <PATH> [--requested-by <ACTOR>] |
Create a read-only directory inspection plan and explicitly grant its scope |
andromeda task list |
List all durable task records |
andromeda task show <TASK_ID> |
Show one task record |
andromeda task evaluate <TASK_ID> [--isolation <LEVEL>] [--confirm-external] |
Evaluate policy without executing any action |
andromeda task record-outcome <TASK_ID> --action-id <ID> --status <STATUS> --evidence <TEXT> --expected-revision <N> |
Record one action's execution outcome and evidence (repeat --evidence for multiple items) |
andromeda task transition <TASK_ID> --to <STATE> --expected-revision <N> [--actor <ACTOR>] [--confirm-external] |
Apply a checked state transition with optimistic concurrency |
andromeda hardware probe |
Print a privacy-conscious hardware report |
andromeda hardware diagnose |
Diagnose driver binding and support-relevant device readiness |
andromeda hardware check <MANIFEST> [--require-tier <TIER>] [--trusted-keys <FILE>] [--allow-unverified] [--artifact-root <DIR>] [--artifact-signing-key <KEY_ID>] |
Probe this host and evaluate one HCM JSON document |
andromeda hardware keygen --seed-file <FILE> [--key-id <ID>] |
Derive the public half of a signing seed, for publishing into a keyring |
andromeda hardware sign <MANIFEST> --seed-file <FILE> [--key-id <ID>] [--output <FILE>] |
Sign an HCM manifest, emitting the manifest with its signature set |
--isolation accepts none, sandbox, micro-vm, and brokered. When omitted, each action
is evaluated at the minimum isolation implied by its own declared risk; when given, it
overrides isolation for every action, which is mainly useful for probing a whole plan under
one level.
Caution
--isolation is a policy-input simulation, not a sandbox proof. A real executor must
supply unforgeable isolation evidence through a future attestation interface.
Exit codes for hardware check:
| Exit code | Meaning |
|---|---|
0 |
The effective tier is usable |
1 |
Refused outright: --require-tier supported|certified without --trusted-keys and without --allow-unverified (input errors such as an unreadable manifest also exit 1) |
2 |
The effective tier is blocked |
3 |
The effective tier is below the tier given via --require-tier |
andromeda-taskd flags: --listen (ANDROMEDA_LISTEN, default 127.0.0.1:7777),
--state-dir (ANDROMEDA_STATE_DIR, default .andromeda/state), --auth-token-file
(ANDROMEDA_AUTH_TOKEN_FILE, default .andromeda/taskd-token), and --capability-keyring
(ANDROMEDA_CAPABILITY_KEYRING, unset by default).
Every path below, including /healthz, requires Authorization: Bearer <token> and
returns 401 unauthorized otherwise. See Local authentication.
| Method | Path | Purpose |
|---|---|---|
GET |
/healthz |
Service status, API version, and the running security posture: authentication (always bearer_token) and capability_admission (unsigned_allowed or require_signed) |
POST |
/v1/tasks |
Validate and create a task |
GET |
/v1/tasks |
List tasks as {"tasks": [...], "warnings": [...]}; a corrupt record file is skipped and reported in warnings instead of failing the whole listing |
GET |
/v1/tasks/{id} |
Read one task |
POST |
/v1/tasks/{id}/capabilities |
Grant capabilities to an existing task; each new capability must have issued_to == plan.task_id and be currently active; takes expected_revision |
POST |
/v1/tasks/{id}/outcomes |
Record one action's execution outcome and evidence; appends an outcome_recorded event and bumps the revision. Allowed only while Running/Verifying, at most one per action (append-only), and the action must belong to the plan |
POST |
/v1/tasks/{id}/evaluate |
Evaluate without executing; isolation is resolved per action, and the result is appended as an evaluated event, bumping the revision |
POST |
/v1/tasks/{id}/transition |
Revision-checked state transition; the Ready, Running, and Succeeded edges are gated. Ready -> Running re-runs policy using the confirmation the request carries (default absent) and rejects unconfirmed L3 side effects; Verifying -> Succeeded requires an evidenced successful outcome for every action |
Errors are uniformly {"error": <code>, "message": <text>}:
| HTTP | error |
Trigger |
|---|---|---|
| 400 | bad_request |
Task id is not a valid UUID, etc. |
| 401 | unauthorized |
Missing, malformed, or wrong Authorization: Bearer token |
| 403 | forbidden_host |
Host is not loopback |
| 404 | not_found |
No such task |
| 409 | already_exists |
Duplicate task_id |
| 409 | revision_conflict |
Stale expected_revision |
| 422 | external_confirmation_required |
Ready -> Running was attempted without confirmation while the plan contains L3 external side effects |
| 422 | missing_evidence |
Verifying -> Succeeded was attempted while some action lacks a recorded outcome, has an unsuccessful outcome, or has an outcome without evidence |
| 422 | capability_not_admitted |
Under require_signed, a capability was unsigned, signed by an unknown key, malformed, or altered after issuance |
| 422 | invalid_task |
Plan validation failure, illegal state transition, or another policy-gated refusal (ungranted plan, policy-denied action) |
| 500 | internal_error |
Serialization or internal failure |
Request bodies use deny_unknown_fields: a camelCase typo such as expiresAt is rejected
(422) rather than silently dropped. Every TaskService call runs inside
tokio::task::spawn_blocking, so blocking file locks and fsyncs never occupy async workers and
/healthz stays responsive even while the store lock is contended.
Every request must carry Authorization: Bearer <token>, /healthz included. A missing,
malformed, or wrong token gets 401 unauthorized with WWW-Authenticate: Bearer and no
explanation of which of the three it was. The comparison is constant-time.
The guarantee lives in the serve wiring, not in a config check. andromeda_taskd::app
takes an Authenticator by value; that type has no Default, no public fields, and no variant
meaning "no authentication", and every constructor is fallible and rejects an empty or
under-32-character secret. An unauthenticated listener is therefore not representable —
there is no flag, environment variable, or unit directive that can produce one. Authentication
is the outermost layer, so an unauthenticated request is rejected before the Host check, before
body parsing, and before any store lock is taken.
The token file (--auth-token-file) is generated on first start as 32 CSPRNG bytes in hex,
written atomically at mode 0600, and reused across restarts. taskd refuses to start if the
containing directory grants anything to group or other. That protection model — directory
0700, file 0600 — is defined once in crates/andromeda-taskd/src/auth.rs, asserted at
startup, and a unit test asserts the shipped andromeda-taskd.service agrees with it. No file
in the repository hardcodes a uid or gid: the service identity is systemd's DynamicUser, and
systemd is the only creator of the directory.
Operationally this narrows the caller set from "any local process or user" to "the service account and root":
curl -H "Authorization: Bearer $(sudo cat /run/andromeda-taskd/token)" \
http://127.0.0.1:7777/healthzImportant
This does not defend against an attacker who already has root or the service account, and
it is not remote authentication or user identity. The token is a single shared secret, so
it cannot distinguish one caller from another and is not yet usable as a policy subject.
A Capability may carry a detached ed25519 signature from a trusted issuer. /healthz reports
which mode is in force:
unsigned_allowed(default, and the mode the shipped image runs in) — unsigned capabilities are accepted. Not a security boundary: an authenticated caller still mints its own grants.require_signed— enabled by--capability-keyring, a JSON file of{"key_id": "<64 hex chars>"}. Both the create and grant paths then reject any capability that is unsigned, signed by an unknown key, malformed, or altered after issuance, with 422capability_not_admitted. An empty keyring fails startup rather than silently rejecting everything while looking hardened.
Important
Signatures do not by themselves close the self-issuance gap: whoever holds the private key is the issuer, and no component in this repository issues capabilities yet — which is why the shipped image deliberately configures no keyring. The gap closes when a trusted host component owns the key and the requesting process cannot reach it. See threat model §4.2 and §6.2.
The signature field is optional, so an unsigned capability serializes byte-identically to
earlier versions and existing task records keep parsing after an upgrade. Verification always
runs after the MAX_TASK_CAPABILITIES length bound, so an unbounded caller-supplied vector
can never force unbounded ed25519 work.
taskd validates the Host header of every request (falling back to :authority under
HTTP/2), accepting only localhost and literal loopback IPs (any of 127.0.0.0/8, [::1], and
their IPv4-mapped forms) with an optional port; everything else gets a 403. Even if a malicious
page uses DNS rebinding to resolve its own name to 127.0.0.1, the request still carries the
attacker's Host and is rejected.
Caution
Host validation only defends against browser-originated DNS rebinding; it is not
authentication, and it does not protect a non-loopback binding. Any non-browser client can
simply send Host: localhost and pass — it still needs the bearer token, but the two checks
are independent and neither substitutes for the other. If ANDROMEDA_LISTEN is changed to a
non-loopback address, the whole API sits on that network behind a single shared secret
designed for same-host callers. Do not bind taskd off loopback.
- One formatted JSON file per task revision, followed immediately by compaction, so in the
steady state each task keeps one revision file plus one
{task_id}.latestpointer file; - reads follow the O(1) pointer first and fall back to a directory scan when the pointer is missing, unparseable, or dangling (compatible with older stores and crash windows);
- an exclusive cross-process lock (fs4); temp file write → flush →
sync_all→ atomic rename, with an additional directory sync on Unix; - write ordering guarantees crash safety: the revision file is durable before the pointer advances, and compaction runs only after the pointer points at a survivor, so the pointer can never dangle at a missing file;
- optimistic concurrency on revisions; every state change, capability grant, and policy evaluation appends an event;
- crash-orphaned
.{uuid}.tmpfiles are cleaned up inside the exclusive lock when the store opens; - a single plan holds at most 10,000 actions; structural validation (duplicate ids,
dangling dependencies, cycle detection) has a single implementation in core's
ActionPlan::validate, which uses an iterative Kahn topological sort so deep chains cannot overflow the stack.
Note
Compaction actively deletes superseded revision snapshots, so the revision files on disk are
not an append-only history (the full audit trail lives in the events embedded in the
latest record). This was never meant to be an append-only ledger resistant to a physical
administrator; a real audit ledger needs signatures, hash chaining, key rotation, a privacy
deletion policy, and independent export.
os/ turns the Rust control plane into an x86-64 UEFI installable image based on
Fedora bootc 44 + KDE Plasma.
Warning
The ISO's default boot entry starts the graphical Anaconda installer. The second entry
is destructive automation for CI: it wipes the first installation disk and must never be
selected on a machine with data.
Building with INSTALLER_DEFAULT=1 inverts the GRUB default and names the output *-ci.iso,
which must never be distributed as a developer preview image.
On an x86-64 Linux host with Podman and privileged containers:
sudo os/scripts/build-iso.shThe result is output/Andromeda-Developer-Preview-x86_64.iso, a SHA-256 checksum, and a
*.manifest.json binding the ISO checksum, payload digest, pc_x86_64 platform variant, boot
provider, and hardware enablement profile. Installer preflight rejects Apple hardware and
architecture or payload-identity mismatches; Mac variants require separate guarded images.
sudo env INSTALLER_DEFAULT=1 os/scripts/build-iso.shsudo os/scripts/test-install.shThe test boots the ISO with UEFI, automatically installs onto a new 64 GiB VirtIO disk, removes the ISO, and starts the installed disk. The installed OS must then:
- have an Andromeda UEFI NVRAM entry plus a standard fallback loader;
- run with SELinux enforcing and reach KDE's SDDM display manager;
- start the loopback-only Andromeda task service;
- generate a hardware report;
- stage and boot revision 2 through bootc;
- stage a rollback and boot revision 1 again.
Beyond the base lifecycle, the installed CI system enters a Plasma Wayland session and
exercises PipeWire, Flatpak, LibreOffice DOCX/XLSX/PPTX/PDF conversion, a real Firefox Wayland
launch, and persistent user data across update and rollback. Success is the serial marker
ANDROMEDA_E2E_OK.
The boot also produces hardware-diagnosis.json; missing boot-critical storage, network,
graphics, or USB-controller drivers block the E2E run.
sudo os/scripts/test-hardware-matrix.shIt boots independent overlays with Q35/NVMe/e1000e/XHCI, Q35/SATA/e1000e, and i440fx/IDE/e1000/UHCI profiles. This validates emulated controller paths; physical hardware remains gated by exact-machine HCM evidence.
On a disposable Google Compute Engine N2 host with nested KVM:
sudo env ANDROMEDA_SOURCE_REVISION="$(git rev-parse HEAD)" os/scripts/test-gcp-nested.sh "$PWD" "$PWD/output"GCP provisioning, evidence retrieval, and guaranteed instance deletion are handled by the
in-repo wrapper os/scripts/gcp-run-e2e.sh, which creates a single labeled instance with
--max-run-duration and deletes it from an EXIT trap. See
Daily Driver Candidate E2E.
Upstream contracts are listed in os/README.md.
blocked < community < reference < supported < certified
reference sits below supported because it is backed by virtual (L0–L2) evidence only,
while supported and certified require physical-machine certification.
| Category | Current product status |
|---|---|
| QEMU/KVM x86-64 + OVMF | Daily Driver Candidate; automated install/desktop/update/rollback acceptance |
| QEMU NVMe/SATA/IDE + e1000e/e1000 | Phase 1 pairwise driver matrix |
| Selected x86-64 PCs | Next-stage Developer Preview candidates |
| Uncertified generic PCs | Community — probing is not support |
| Non-T2 Intel Macs | Per-model pilot |
| T2 Intel Macs | Experimental |
| M1/M2 Macs | Separate Asahi Preview candidate |
| M3 and newer Apple silicon (including M5) | Watch; requires the corresponding Asahi model page and installer, with no delivery commitment |
An HCM is a JSON document declaring selectors, requirements, kernel channels, artifacts, and
evidence. The current schema version is 2 (schema in
schemas/hardware-compatibility-manifest.schema.json,
example in examples/hcm/). An unknown schema version is rejected before
evaluation, rather than accepting whatever happens to deserialize.
The matcher is conservative: if any selector or requirement is unsatisfied, effective_tier
drops straight to blocked and the specific reasons are listed in missing. A hardware
report by itself grants no support tier. Shipping products additionally require HCM
signatures and CI evidence verification.
Detailed rules are in hardware, driver, and migration research, hardware enablement engineering, HCM development notes, and the physical hardware certification test plan.
- Model output is always untrusted input; an untrusted plan cannot choose its own risk floor;
- authority is decided by the host Capability Broker and policy, never by natural language;
- deny rules override capability grants;
- a capability is resource-scoped, expires independently, and contains no secret value;
- an L3 external side effect cannot reach
runningunless the transition explicitly carries confirmation; confirmation defaults to absent and is recorded on the state-change event with its actor; - a task cannot reach
succeededunless every planned action has a recorded outcome that succeeded or was skipped and carries at least one piece of evidence; - task writes use atomic replacement, cross-process locking, and optimistic revision checks;
taskdrefuses to bind to a non-loopback address at startup unless explicitly overridden;- every
taskdrequest is authenticated: an unauthenticated listener is not representable in the type system, so no flag, environment variable, or unit directive can produce one; - a capability signature, when a keyring is configured, is verified fail-closed — and always after a length bound, so mandatory verification can never be handed an unbounded input;
- hardware reports omit serial numbers and do not themselves grant a support tier.
- capabilities are still self-issued: the signing and verification mechanism exists, but no
component issues capabilities, so the shipped image runs
unsigned_allowedand an authenticated caller still mints its own grants. Whoever holds the private key is the issuer; taskd's local token is not user identity: it is a single shared secret that separates the service account and root from other local users. There is no remote authentication, no multi-tenancy, and the token cannot serve as a policysubject. TheHostheader check defends browsers against DNS rebinding only and is not authentication;- isolation levels are asserted by the caller, not attested by an execution environment —
the CLI's
--isolationis a policy simulation, not a sandbox proof, and no sandbox exists; - the L3 confirmation is caller-asserted, not broker-attested: it proves a commit point was taken and by whom, not that a human took it;
- evidence is recorded by the executing party; there is no independent verifier;
andromeda hardware checkauthenticates a manifest only when--trusted-keysis passed; without it the manifest's declared tier is self-asserted. Gating--require-tier supported|certifiedon an unverified check is refused outright — see docs/development/hardware-compatibility.md;- there is no real tool executor, so the API must not be exposed to untrusted networks;
- the following are not implemented, and no integration may imply otherwise: model invocation and planner, bubblewrap/SELinux/microVM executor, credential broker, confirmation broker, external connector/MCP broker, signed policy bundles, independent verifier and rollback/compensation executors, a trusted capability issuer (verification exists; nothing issues), user identity and remote authentication, multi-tenancy, and the Task Center GUI.
The full trust-boundary analysis, including the known-unfixed attack surface, is in docs/andromeda-threat-model.md (written in Chinese, matching the docs/ convention).
For security issues, read SECURITY.md. Do not open a public issue for an unpatched vulnerability involving privilege boundaries, credential exposure, filesystem escape, task-policy bypass, unsafe update/recovery behavior, firmware, or destructive hardware actions — use GitHub Security Advisories instead.
| Workflow | Contents |
|---|---|
ci.yml |
cargo fmt --check, clippy -D warnings, cargo test --workspace --locked, the installer platform guard, and the Containerfile layer budget; plus tests and hardware probing across ubuntu / macOS / Windows |
os-e2e.yml |
shellcheck, then the full UEFI install → first boot → update → rollback run, uploading the ISO, checksum, and serial evidence |
Action pinning policy: first-party actions/* are pinned to major tags; third-party actions are
pinned to a full commit SHA with a trailing version comment.
Recommended before opening a PR:
cargo fmt --all -- --check && cargo test --workspace --locked && cargo clippy --workspace --all-targets --locked -- -D warnings && git diff --checkNear-term engineering order:
- Signed HCMs, pre-installation preflight, and QEMU/real-PC CI;
- bootc/OCI + OSTree images, power-loss-safe updates, and a recovery environment;
- Plasma/KWin Task Center adapter and the Capability Broker daemon;
- Attested bubblewrap/SELinux sandboxes and a microVM executor;
- Steam/Proton managed domain, Windows Workspace, Office/format routing;
- Windows/macOS migration scanners;
- A separate M1/M2 Asahi Preview.
Full phases, SLOs, and the first 12-week plan are in the product development plan.
Overview
Development
- Developer getting started
- Task control plane
- Developer Preview installation and acceptance
- Daily Driver Candidate and GCP E2E
- Hardware Compatibility Manifest
- Hardware enablement and the automated matrix
- Physical hardware certification test plan
Research
- Windows gaming, Office, and file formats
- PC/Mac hardware, drivers, and migration
- Reliable updates, isolation, and AI agents
- Desktop platform and distribution engineering
- Open-source component adoption matrix
Decisions and reviews
Most documents are written in Chinese. This README and README.zh-CN.md are kept in sync.
Read CONTRIBUTING.md first. In short:
- one architectural concern per PR;
- explain the user outcome, security boundary, failure path, and validation;
- add tests for every state transition, policy rule, parser, and compatibility decision;
- do not silently expand a hardware tier or application compatibility claim;
- do not add privileged execution, network listening beyond loopback, credential access, firmware writes, disk mutation, or external side effects without a threat model and explicit maintainer review;
- drive material system design through ADRs (template:
docs/adr/0000-template.md); - on the Rust side: MSRV 1.85, unsafe forbidden workspace-wide, and public fallible functions document their error conditions.
When using Codex/Claude Code for research, development, installation E2E, failure diagnosis, or
releases, the in-repo
$andromeda-os-engineering skill is available. It
carries the project boundaries, test and merge gates, GCP lifecycle rules, and known failure
defenses, and ships read-only repository/PR audit scripts.
New dependencies must carry a compatible license and a clear upstream source. Firmware, fonts, codecs, model weights, proprietary SDKs, and data files require artifact-level review rather than assumptions inherited from the surrounding project.