Skip to content

Search is only available in production builds. Try building and previewing the site to test it out locally.

Launcher, trust, plugins-sdk and manifest-signer are four println! calls — and they are the entire security boundary

Источник: dev/fera-research/docs/oc3/005-launcher-trust-plugins-are-skeletons.md (read-only; сгенерировано scripts/sync-docs.mjs)

Launcher, trust, plugins-sdk and manifest-signer are four println! calls — and they are the entire security boundary

Section titled “Launcher, trust, plugins-sdk and manifest-signer are four println! calls — and they are the entire security boundary”
  • kind: problem
  • area: trust
  • severity: high

Full contents of the four files that hold the project’s entire security posture:

core/crates/trust/src/lib.rs — 8 lines, entire file:

//! Trust — manifest parsing, signature verification, trusted-key store, version/targeting rules (`abi_version` / `api_edition` / `module_version` axes).
//!
//! Hash mismatch is a warning, never a load refusal (ADR-004);
//! the manifest itself is the real security boundary (ADR-003).
//!
//! Design refs: TECHSPEC §7, ADR-003/004/021.
// Placeholder — implementation lands per TECHSPEC §10 sequencing.

launcher/src/main.rs — 20 lines; :17-20:

fn main() {
// Placeholder — see TECHSPEC §7 "Launcher" for the algorithm.
println!("fera-launcher: pre-implementation skeleton");
}

plugins-sdk/src/lib.rs — 10 lines; :10:

// Placeholder — implementation lands per TECHSPEC §10 sequencing (step 8).

There is no .wit file anywhere in the repository. TECHSPEC.md:327-334 specifies two WIT interfaces (plugin-readonly, plugin-write); neither exists.

tools/manifest-signer/src/main.rs — 12 lines; :10-12:

fn main() {
println!("fera-manifest-signer: pre-implementation skeleton");
}

core/crates/trust/Cargo.toml:12 — [dependencies] empty. No ed25519, no sha2, no serde_json. plugins-sdk/Cargo.toml:12 — [dependencies] empty. No wasmtime, no wit-bindgen, no wasm-tools. grep -c "^name = \"ed25519\"" ~/dev/fera/fera/Cargo.lock → 0. grep -c "^name = \"wasmtime\"" → 0. grep -c "^name = \"serde_json\"" → 0.

No network I/O exists anywhere in the workspace. No reqwest, no hyper, no curl binding; launcher/Cargo.toml:12 [dependencies] is empty.

ADR-003/004/013/021 make the signed manifest the real security boundary, and ADR-013 grounds the entire launcher design in a concrete, exploited precedent — Discord’s unsigned, user-editable settings.json update endpoint, abused by unofficial tools to redirect module downloads (ADR.md:388-391). QA.md:168-170 requires an explicit test that “no plaintext, user-editable file controls the update endpoint”. QA.md:133-135 requires that the manifest be signature-verified before any of its contents are trusted.

None of this can be verified today because none of it exists. There is no manifest parser, no verifier, no key store, no HTTP client, no update path, and no plugin host. The four files that would constitute the trust boundary are placeholders that print their own name and exit 0 — and fera-launcher exits successfully, which for a launcher whose entire job is to refuse to run unsigned code is the wrong default posture to hand someone.

This is not an implementation-nagging finding — the repo is pre-implementation by its own README ("Pre-code" / "Alpha development (no test builds yet)"). It is a sequencing finding with one specific consequence worth stating:

ADR-004 (ADR.md:104-107) is the single most consequential design decision in the trust story — hash mismatch is a warning, not a refusal — and it is stated as a doc comment on an empty file (trust/src/lib.rs:3-4). It is a deliberate loosening of a security gate, justified by user freedom to self-modify. A loosening that deliberate needs to be implemented carefully and tested deliberately (ADR-004 itself implies the manifest signature is what carries the security, ADR.md:110-113), not transcribed as a comment. The design also has three distinct refusal classes with only one documented as a warning: signature-invalid (refuse), below-minimum-version (refuse, QA.md:152-153), version-incompatible (fallback, QA.md:126-128). Only hash-mismatch is a warning. Whoever implements this will have to make the other three explicit.

Варианты решения (с tradeoffs)

Section titled “Варианты решения (с tradeoffs)”

Option A — leave as placeholders until §10 step 6.

  • Cost: nothing now.
  • Benefit: honest; step 6 of §10 names exactly this work (“Search (milli-fork), encryption tiers, trust manifest + launcher”).
  • Risk: the cross-cutting trust rules (ADR-004 warning-not-refusal; ADR-002 capability tiering; ADR-021 signature-over-whole-entry) are currently enforced nowhere and are invisible to any reader of the code.

Option B — write the trust crate’s tests first, against a not-yet-existing verifier.

  • Cost: writing tests for unimplemented code risks pinning a wrong contract.
  • Benefit: the QA gates at QA.md:121-159 are behavior specs that survive independent of implementation; writing them as executable tests before the implementation makes the spec the source of truth.

Option C — build a fake-but-real crypto/manifest path now (serde_json + ed25519 + a golden manifest) to de-risk the canonicalization decision.

  • Cost: pulls in 3 dependencies, all trusted, all pure-Rust.
  • Benefit: makes the two-incompatible-schema problem (docs/oc3/003) and the undefined-canonical-serialization problem concrete before the launcher has a user, rather than after.

A for the implementation, C for the one blocking decision, and B for the refusal-class table.

The one thing that is worth doing now, at this scale, before any code exists, is to settle the refusal taxonomy — because it is a pure design decision with no implementation cost and it is the thing the whole trust story hinges on. Write down (as an ADR addendum or a TECHSPEC §7 table):

Condition Action
Manifest signature invalid / untrusted key refuse to load any module
Manifest expired, or module below min allowed version refuse (QA.md:152-153) — but surface, not silently ignore
abi_version / api_edition / host.max_version incompatible fallback to builtin (QA.md:126-128, TECHSPEC.md:384)
On-disk artifact hash ≠ manifest hash warn, load (ADR-004) — the documented exception

That table makes the fact that exactly one of four gates is a warning an explicit, reviewable decision rather than an accident of which rule got written down. Everything else can wait for §10 step 6 per the repo’s own (correct) sequencing.

Separately: plugins-sdk and trust both have an empty [dependencies], so even the intended crates (wasmtime, ed25519, serde_json) are not pinned or even selected yet. That is fine for placeholders, but it means the dependency choices for the security-critical path have not been made at all. Worth deciding the ed25519 crate (or ring) choice early since it constrains the manifest format.

  • docs/oc3/003-manifest-two-incompatible-schemas.md — the schema the trust crate must parse; two incompatible versions stated 11 lines apart
  • docs/oc3/006-lattice-not-consumable.md — the other half of the plugin tier (also empty of enforcement)