Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Air Engineering Principles

Founding document — Version 1.0

Air is a desktop infrastructure stack. The quality of its foundations determines the trust developers will place in the whole. Eleven principles guide every contribution to Air’s code. They are distinct from the Project Charter (which sets out values) and from the ADRs (which record technical decisions): the Engineering Principles concern method, the how of building.

These principles are immutable. ADRs and specifications conform to them by construction. All code integrated into the Air project respects these principles.

Principle 1 — Exhaustive testing of foundations

Layers 0 and 1 are the foundations of everything above. No bug in these layers is acceptable. Test coverage at 100% (lines + branches), including unit tests, property-based testing, integration tests, systematic fuzzing on any API that accepts external data (parsing, deserialisation, buffers).

Complex testing strategies accepted if necessary: custom test harnesses, syscall simulators to isolate tests from kernel conditions, fault injection to validate error paths, concurrent stress tests. Time spent designing a solid testing strategy is time well invested, never wasted.

Higher layers (2 and above) aim for very high coverage (> 90%) but may have leeway depending on the nature of the code (pixel-perfect UI testing, for example). Rigour remains, but the objective is no longer strictly 100%.

Principle 2 — Defensive arithmetic

Every numeric computation is designed with awareness of the type’s size. Every operation that could overflow uses checked_*, saturating_*, or wrapping_* variants explicitly chosen according to the intended semantics — never the naked operation, which panics in debug and wraps silently in release.

Numeric type conversion: as forbidden for potentially lossy conversions. Use TryFrom or try_into which return Result. Cases where a lossy conversion is intentional must be commented and localised.

Lints clippy::cast_possible_truncation, clippy::cast_sign_loss, clippy::cast_lossless, clippy::arithmetic_side_effects set to deny in layers 0 and 1. Set to warn minimum in higher layers.

Principle 3 — Strings, buffers, encodings: zero presumption

All string handling explicitly distinguishes between bytes (&[u8], Vec<u8>) and valid UTF-8 (&str, String). No silent conversion. from_utf8 (which returns Result) systematically, never from_utf8_unchecked except in justified and localised cases.

Buffers: size always validated before use, no presumption on length. Slicing with get() which returns Option rather than direct indexing which can panic. Indices always bounded and checked.

Encodings: explicit everywhere. No “default string, the OS handles it”. For file paths, explicit distinction Path/OsStr (which can contain non-UTF-8 on Unix) and str when we know it is UTF-8. Explicit conversion with error handling.

Parsing of external data: schema-first when possible (Cap’n Proto for AirCom, explicit validation elsewhere). No unwrap() on parsing results in production code.

Principle 4 — Validation upfront, logic afterwards

Every public function validates its parameters at the start, before any processing. Expected invariants are made explicit, verified, and return Result (or Option) on violation. The function body can then presume the invariants hold and focus on business logic.

Favour the “parse, don’t validate” pattern: transform inputs into types that carry the validity guarantee in their type, rather than validating in multiple places. A function that takes an EmailAddress (validated newtype) rather than a String need not revalidate.

Over-testing parameters is acceptable in layers 0 and 1; we will optimise after measurement. Never again a bug because we forgot to validate a case.

Principle 5 — Optimise after measurement, never before

Principles 2, 3, 4 produce deliberately defensive code. This code may be slower and more verbose than its optimistic version. That is assumed.

At the end of every cycle (end of phase, or major milestone), a performance audit is conducted: profiling on representative use cases, identification of hotspots, measurement of time spent in validations versus business logic. If — and only if — a validation is measured as a significant cost on a hot path, we may consider moving, lightening, or removing it, with written justification and an additional test guaranteeing that the invariant holds by construction.

The flow direction is: over-secure then trim after measurement, never under-secure then harden after bug. The opposite, seen too often, produces CVEs.

Principle 6 — The 80% rule on dependencies

Air relies on existing Rust crates when they are quality, mature, and useful. For each candidate dependency, the rule is: if less than 80% of the crate’s code is effectively used by Air, the dependency is refused. Instead, we integrate only the strictly necessary code, adapt it to Air’s style, and turn it into an Air sub-module or crate that we maintain ourselves.

Justification: dead code in a dependency is not neutral. It continues to be compiled, must be maintained (security updates, adjustments for new Rust versions), exposes an attack surface (CVEs on code we do not use but ship), and inflates the binary. A dependency used at 10% is a 100% maintenance debt for someone.

This principle is evaluated by explicit audit at each dependency addition, documented in a DEPENDENCIES.md file per Air crate: name, version, % of API used (estimated), justification. Regular review (at each phase end) to detect dependencies whose usage has dropped.

Explicit exceptions are possible for very mature and structuring crates where the ratio can be lower if the dependency is universally needed and impossible to reproduce reasonably. These exceptions are named and justified in EXCEPTIONS.md, not implicit. Example recorded: icu4x adopted as an explicit exception (see ADR-016) as the worldwide Unicode reference whose reproduction is not reasonably possible.

Principle 7 — Verbosity in service of clarity

Air favours explicit verbosity when it clarifies data flow, typing, invariants, and cost. No hidden magic. No introspective runtime that invalidates code reading. No multi-level abstractions when one suffices. Air’s code must be readable by a developer discovering the project, without prior knowledge of a “magical” framework.

This preference shows in the design of public APIs: explicit subscriptions rather than implicit by tracing, explicit conversions rather than implicit by dereferencing, dependencies passed as arguments rather than injected in the background. A few extra characters to write for hundreds of times fewer surprises to understand.

Principle 8 — Contractual stability

Every air-stable symbol (see ADR-012) must have ABI conformance tests in addition to functional tests. The test loads a reference binary compiled against an earlier version and verifies that it continues to run against the current version. These tests are kept in CI for all supported versions.

The ten-year ABI stability commitment is not wishful thinking: it is tooled. The tools air-abi-check, air-symver, air-deprecation-tracker (introduced from phase 0) automate verification. A change that would break the ABI on an air-stable symbol is rejected in CI even before human review.

The air-internal and air-experimental zones do not have these constraints: they allow rapid evolution between major releases. ABI discipline is concentrated where it is promised.

Principle 9 — Modest and diversified hardware targets

Air is continuously developed and validated on modest and diversified hardware (Raspberry Pi 4 4 GB and 8 GB in ARM64, Mac mini Intel i5 8 GB and MacBook Pro Intel i7 16 GB in x86_64, in initial phase, expanded via the ADR-014 catalogue). This constraint is deliberate: it forces each component to respect its memory and CPU budgets, guarantees the relevance of multi-architecture (ARM64 + x86_64) from day one, validates the diversity of GPUs and drivers, and ensures that the hardware longevity commitment (Charter, principle 4) holds in practice across varied classes of machines.

If Air does not run correctly on the reference machines, it is Air that adapts, not the hardware target that changes.

This discipline has a less obvious positive effect: it protects Air against the well-known syndrome of software developed exclusively on powerful machines, which becomes imperceptibly heavy because developers do not feel the heaviness. A team developing on Raspberry Pi 4 knows immediately when something slows down.

Principle 10 — Compartmentalisation and privilege separation

Every component runs with the strict minimum it needs, and under confinement — including Air’s own components. A security mechanism that steps aside for its own does not protect the system: it protects others from the system. Default confinement is minimal and barely permissive; the envelope widens on explicit grant, never by belonging.

Privilege reduction is correct, or it does not count. Dropping root is not a matter of calling setuid: supplementary groups must be dropped, then real, effective and saved must be set for the group before doing so for the user — set_groups, set_resgid, set_resuid, in that order. A bare setuid leaves the saved-set intact, hence the ability to regain what one believed relinquished. It is a classic privilege-separation flaw, and layer 0 exposes these calls precisely so that no one has to do without them.

Privilege separation is an architectural decision, not a deployment option. Code that parses hostile data holds nothing: the process talking to the peer before authentication works inside a cage, and the gestures it cannot perform itself are mediated by a supervisor that never touches the network. A component demanding a privilege usually reveals that a mediation is missing — not that the privilege should be granted.

Confinement is monotonic: inherited across execve, it can only narrow. Two practical consequences. The cage must be applied before the first byte read from a hostile source; afterwards it is too late. And PID 1 is the one acknowledged exception — not as a favour, but because confining it would irreversibly cap everything the machine later launches. It draws its protection from being minimal and verified, not from being caged.

Finally, a confinement that cannot be applied is no licence to do without it. Profiles are fail-closed: whatever is not explicitly permitted is refused, and a failure to apply is a refusal to run — never an unconfined run.

This principle’s least visible effect is the one that matters most: it bounds the consequences of a bug. Air aims for zero bugs in its foundations (Principle 1), but a system that only holds provided it is faultless is not a safe system. Compartmentalisation is what keeps a mistake a mistake, instead of letting it become a compromise.

Principle 11 — Strict input, readable output

Air minimises text parsers. Everything the system consumes as input — configuration, manifest, grant, protocol message — rests on a fixed, bounded-layout schema: at this position, these admissible values, nothing else. What remains is then no longer parsing proper but a presence check: it is there, or it is not.

The reason is that tolerance is the flaw. A computer never errs comparing two bytes: they are equal or they are not. Trouble begins with strings, where comparing is no longer enough — one must check that a byte sits at the right position, that nothing spurious precedes it, that a non-breaking space pasted from a web page does not change the meaning. A textual format therefore always demands a tolerant parser, and every tolerance is a place where two readers may diverge. Where two readers diverge, there is a breach.

Air holds that the plain-text configuration file is Unix’s worst idea — reproduced identically on Solaris, HP-UX, System V and their descendants — conceded to please the user and paid for in machines that no longer boot because of one stray character.

It is not binary that brings the property, it is the fixed layout. A hand-rolled binary format — length fields, offsets, arrays sized by the caller — is worse than text: the same ambiguities, without the readability to spot them, and software history’s gravest overflows live in binary parsers. Binary is the consequence of a bounded schema, never its justification. Writing “binary” without “fixed, bounded layout” licenses the home-made TLV whose lengths come from the attacker.

Output obeys the opposite rule, and that is a commitment. Making system state readable by a human is legitimate: Air’s commands can produce text and JSON on demand. Readability is a service rendered on output, where the system is the source and no ambiguity is possible. On input, no string is taken at its word: it is parsed strictly, without tolerance, or it is refused.

When text must nonetheless come in — a developer writes their manifest, an administrator imports a configuration — it does not enter through the execution door. A dedicated tool compiles it into a verified artefact, off the critical path, and the deciding component only ever reads the schema. The parser therefore exists, but it is displaced to where its failure is merely a refused import, never a compromise.

The least visible effect is the one that matters most: a system with no reading tolerance is a predictable system. It does not do what one meant, it does what is written — and “doing what it says” is exactly what Air promises.


Application and evolution

These eleven principles are immutable. ADRs and specifications conform to them by construction. Any evolution would require a dedicated RFC and exceptional community consensus.

The principles are not slogans: they are operational. They translate into clippy lints, into CI checks, into mandatory code reviews, into per-phase audits. A PR that violates a principle does not pass review, even if the code looks correct otherwise.


Document licence: MPL 2.0 Status: Founding document, immutable.