Watch
1
0
Fork
You've already forked SouveraineOS
0
SouveraineOS/docs/souveraine-components/machined.md
Fimeg 4235848bee souveraine-components: per-binary docs + RedFlag-shaped audit tracker
One page each for machined, secrets, sessiond — what each owns, what
crosses its boundary, and the open gaps before it reaches the RedFlag
capability-token bar. Gaps mapped to concrete RedFlag primitives (token,
closure hash, canonical message, KeyID, independent verifier, binary
integrity, eBPF) with file:line anchors, not doctrine-summary hand-waving.
audit-status.md rolls up priority: P0 unlock/handoff attestation, P1
machined token issuance, P2 secrets caller identity, P3 per-agent UIDs,
P4 binary watchdog shared-deferred with RedFlag SEC-022.
2026-07-16 21:48:13 -04:00

5.1 KiB

souveraine-machined

System-tier machine identity daemon. The root of the authority family — it holds the machine's Ed25519 seed and hands out signatures, never key material. Every other component that needs to prove machine identity asks this one to sign.

Source: src/bin/souveraine-machined.rs, src/machined/{protocol,server,signer,client}.rs. RedFlag analog: the server's SigningService (authority that mints and signs) in server/internal/services/capability_minter.go.

What it owns

  • The machine Ed25519 seed at /var/lib/souveraine/seed-id.
  • A Unix socket at /run/souveraine/machined.sock (group souveraine, group-readable/writable).

The seed is a precondition, never created by the daemon. If no seed exists, startup fails loudly. Identity creation lives in the deliberate, guarded CLI verb souveraine machine init. Same doctrine as secrets and sessiond; see SeedId::load in core/identity/seed.rs.

What crosses the boundary

Requests are JSON-lines over the socket. Two verbs:

  • pubkey — the machine's public key (safe to share).
  • sign { domain, payload_hex } — a signature over domain-separated bytes. The signed material is {SIGNING_CONTEXT}:{domain}:{payload}, never caller-supplied raw. domain is restricted to ASCII alnum/-/_/./,, max 64 chars; the daemon refuses anything else.

The private key never leaves the process. Callers get signatures and the public key — that is the entire surface.

Identity of the caller

SO_PEERCRED is read on every connection; uid/gid/pid are logged on every sign request and every refusal (src/machined/server.rs:25-43). Today this is observability, not enforcement — any process in the souveraine group can request a signature. That is the central audit gap (below).

Today's security posture

  • Domain separation prevents the daemon from being turned into an oracle for arbitrary signatures: a signature under one domain does not validate in another. This is the same principle as RedFlag's CanonicalMessage (agent/internal/capability/token.go) — the signed bytes are a fixed, field-delimited string, never caller-controlled in shape.
  • Seeds are never daemon-generated; no background process can quietly mint a machine identity.
  • The socket is group-scoped, so membership in souveraine is the coarse gate.

Audit gaps (toward the RedFlag bar)

The target posture is RedFlag's capability-token model, read from its source (not the doctrine summary):

  • Capability token = Ed25519-signed, forward-only, scoped, expiring. RedFlag's Token carries Version (forward-only — new versions add fields, never reinterpret), Operation, IssuedAt/NotBefore/ExpiresAt, and a Signature (agent/internal/capability/token.go). machined today signs caller payloads but does not issue scoped, expiring tokens — it signs raw domain:payload bytes with no operation, no expiry, no closure. Gap: callers cannot present a machined-issued token to a verifier, because machined mints signatures, not tokens.
  • Closure hash binds the token to exactly what it authorizes. RedFlag's ClosureHash is sha256 over sorted, de-duplicated name@version#sha256 lines, so neither order nor duplicates change the digest. The Souveraine analog for "what is this signature authorizing" does not exist yet — the payload_hex is opaque to the daemon. When tokens land, the authorized thing (a verb, a target binary hash, a session) must be inside the canonical message the way RedFlag's closure is.
  • KeyID binds a signature to a key fingerprint. RedFlag's KeyIDFor is hex(sha256(pubkey)[:16]). machined exposes the pubkey but does not advertise a fingerprint verifiers can pin; a rotating seed has no discoverable, comparable identity today.
  • The verifier must be independent. In RedFlag the privileged executor (helper/) verifies the token itself — it does not trust the agent's claim. Souveraine's analog: whatever consumes a machined signature must verify it against the pinned pubkey, not trust the relay. No such verifier is wired yet.
  • SO_PEERCRED is logged, not enforced. Logging is necessary for audit (P3: use is visible) but not sufficient as a gate. Per-agent UIDs (the services-infrastructure review) make SO_PEERCRED kernel-backed caller identity, which is what the token's AgentID field is in RedFlag — but today every caller is uid 1000.

Binary integrity of machined itself

RedFlag's binary-attestation primitive is verify-binary <path> <expected_sha256> (helper/src/main.rs:608-667) — SHA-256 compare, exit EXIT_INTEGRITY=21 on mismatch. It is deliberately not wired in RedFlag either (SEC-022: "kill-on-mismatch and phone-home are deliberately not wired"). machined has no self- or peer-integrity check today. When this lands, the same SHA-256-compare shape applies, and — as in RedFlag — the DAC/sudoers layer (root:root 0755, non-overwritable by the running user) is the integrity guarantee that holds until the watchdog is wired.

See audit-status.md for where these land in priority order.