Skip to content

Fleet Studio B2b · slice 1 — Grant foundation: Enroll seeds lifecycle-admin + self-owned control issuance #1541

Description

@jeonghun-jj-lee

Important

Problem — The control-grant machinery (scopes, state machine, write gate) exists but is dormant and unseeded: no flow mints a control grant, and lifecycle-admin authority is never established — so a headless peer can never be approved from anywhere. See #1540 for the full design and the independent-peer model.
Approach — Seed lifecycle-admin authority at Enroll (record the enroller as the target's authorityIdentityKey, riding the identity-bound enrollment nonce + Observe bootstrap), and extend the self-owned fast-path (the evaluateObserveBootstrap pattern) to mint a control grant on an explicit enable act. Control lands per-session and is never auto-restored.
Scope — in: enroll seeding of lifecycle-admin; self-owned control issuance; per-session re-arm semantics. · out: the write plane (#1540 slice 2); UI (slice 4); the shared handshake (slice 5).
Assumptions — Enroll already establishes bilateral identity + reader-token Observe; both machines are self-owned by one operator.

Warning

Deliberate-review corrections (2026-09-24) — these BIND over any stale text below.

  • lifecycle-admin authority is NET-NEW persistence, not a wiring. Today authorityIdentityKey exists only as an injected parameter to the pure isLifecycleAuthority predicate (fleet_control_lifecycle.ts:291/303) — no store, no writer, no resolver. This slice must DEFINE: (a) a persisted authority record, (b) a writer invoked at amico fleet enroll recording the enroller as the target's authority, (c) a resolver answering who holds authority for machine X. The enroll verb (amico-run/fleet_enroll_verb.ts) has no bootstrap/nonce hook today — wiring it is in scope (integration surface, not "unit-testable, no UI").
  • Self-owned control is a NEW evaluateControlBootstrap, not an extension. evaluateObserveBootstrap explicitly refuses a control outcome ("there is NO control outcome here — deliberately NOT implemented"). Author a new predicate mirroring its structure; do not "extend" the observe one.
  • management-verified must be a defined predicate. It is a bare boolean set true only in tests today. Name the identity/transport/token check that establishes it, as an AC.
  • AC3 is a regression-guard, not new work — control-not-auto-restored is already satisfied by landed fleet_headless_rehydration.ts.
  • Deliver the requester-side, owner-resolvable, token-bearing grant view Fleet Studio B2b · slice 2 — Observation write plane: control-gated remote writes #1542 needs: the issued control grant records targetMachineId and its token on the controlling machine, plus a read that resolves a grant by targetMachineId === <owner> (grants already carry targetMachineId).
    Full record: ADR 0034 decision record (D2/D3).

Acceptance Criteria

  • amico fleet enroll records the enroller as the target's lifecycle-admin authority (the authorityIdentityKey is persisted and resolvable afterward).
  • A self-owned, management-verified peer can mint an active control grant via an explicit enable, with no target-side interaction.
  • A control grant is not auto-restored on rehydration — it lands suspended until an explicit re-enable.
  • A headless target that ran Enroll yields a lifecycle-admin authority resolvable from the enrolling machine (the target renders nothing).
  • A shared (non-self-owned) peer does not get the self-owned fast-path — it is held for approval (no privilege bleed).

Testing Decisions

Extend the existing unit suites for fleet_observe_bootstrap (self-owned vs shared decision), fleet_control_lifecycle (grant issuance/state), and fleet_headless_rehydration (control-never-auto-restored). Add an enroll-seeds-lifecycle-admin test. Reuse fleet_two_peer_e2e for the two-machine grant path. Tests assert external behavior (a grant exists/active/suspended; an authority resolves), not internals.

Key Decisions

  • The self-owned control fast-path mirrors evaluateObserveBootstrap — self-owned + verified management access authorizes the explicit enable; every other case requires approval.
  • lifecycle-admin is not a superset of control — it mints/revokes/re-admits, it does not itself drive sessions.
  • Authority is seeded at Enroll, not minted at runtime — this is what makes headless peers approvable from elsewhere.

Constraints & Invariants

  • Control is never auto-restored (every restart/reconnect leaves it suspended until explicit re-enable).
  • Headless peers enforce only — they never render approval.
  • No privilege bleed — a shared peer can never borrow the self-owned fast-path.

Prior Art

Read first (modules): fleet_observe_bootstrap (the self-owned/shared decision + reciprocal grant issuance), fleet_control_lifecycle (scopes, grant state machine, isLifecycleAuthority), fleet_headless_rehydration (the control-suspended-on-rehydration rule), fleet_enrollment_nonce + the mint route (identity-bound bootstrap to extend for lifecycle-admin seeding).

Source

Notes

This is the security-critical root of the trust model → HITL. It carries no UI and is fully unit-testable. The ADR + CONTEXT.md (already authored) ride this slice's PR.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    hitlNeeds human review before merge

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions