pub struct RuntimeService { /* private fields */ }Expand description
Service-layer wrapper around arkhe_kernel::Kernel. Builds a
kernel with WAL configured and exposes a forge-shaped dispatch API.
Implementations§
Source§impl RuntimeService
impl RuntimeService
Sourcepub fn new(world_id: [u8; 32], manifest_digest: [u8; 32]) -> Self
pub fn new(world_id: [u8; 32], manifest_digest: [u8; 32]) -> Self
Construct a service backed by a chain-only WAL writer (L0
SignatureClass::None). world_id and manifest_digest are
pinned into the WAL header.
Sourcepub fn register_action<A: Action>(&mut self)
pub fn register_action<A: Action>(&mut self)
Register a forge ArkheAction so the kernel will execute it
when scheduled. Any forge action whose type bears
#[derive(ArkheAction)] automatically satisfies the kernel
Action bound through the derive’s emitted kernel-side
stack.
Sourcepub fn create_instance(&mut self, config: InstanceConfig) -> InstanceId
pub fn create_instance(&mut self, config: InstanceConfig) -> InstanceId
Create a fresh kernel instance and return its InstanceId.
Sourcepub fn dispatch<A>(
&mut self,
instance: InstanceId,
principal: Principal,
action: &A,
at: Tick,
caps: CapabilityMask,
authenticated_actor: Option<ActorId>,
) -> Result<StepReport, DispatchError>where
A: Action,
pub fn dispatch<A>(
&mut self,
instance: InstanceId,
principal: Principal,
action: &A,
at: Tick,
caps: CapabilityMask,
authenticated_actor: Option<ActorId>,
) -> Result<StepReport, DispatchError>where
A: Action,
Dispatch a forge action — inject the authenticated actor through the
kernel actor channel, run the L2 GDPR admission gate on it,
postcard-encode the action’s canonical bytes, submit at tick at
under the caps ceiling, then step the kernel once with caps as
the operator session ceiling. Returns the kernel’s StepReport so
the caller can inspect actions_executed / effects_applied /
effects_denied.
§Capability ceiling
caps plays both kernel roles of this single-shot dispatch: it is
recorded on the Submit record as the submission ceiling (bounding
the action and any children it schedules) and passed to step as
the operator session ceiling. The kernel resolves the executing
action’s authority as effective_caps(default_caps, principal, caps) ∩ caps — Principal::System holds no blanket bypass, so an
instance whose default_caps lacks a SYSTEM-gated capability
(ScheduleAction / SendSignal) denies it for every principal.
§Single source of truth for the acting actor
authenticated_actor is the caller identity the integrator’s
auth / session layer (which sits ABOVE forge) has already verified —
e.g. resolved from a login session, bearer token, or passkey
assertion. None denotes a system / anonymous caller with no
authenticated actor.
dispatch threads this actor into Kernel::submit’s actor channel
(as Option<EntityId> via ActorId::get). The kernel records it in
the WAL Submit record and replays it into KernelActionContext::actor, which
the arkhe_forge_core::bridge injects as the forge
ActionContext::acting_actor. A user-scoped compute body reads its
acting identity from THAT channel and stamps it into the stored record
(SpaceConfig.creator, ActivityRecord.actor) — there is no
wire-controlled actor field to substitute, so the C3
actor-substitution attack is structurally impossible. A user-scoped
action submitted with authenticated_actor == None is rejected inside
compute (the bridge collapses the rejection to no Ops — the WAL holds
the Submit/Step envelope but no effect materializes). A system action
that does not read acting_actor proceeds with None.
§GDPR ErasurePending admission gate (C3)
The kernel compute path drives a forge action through a viewless
ActionContext (see arkhe_forge_core::bridge), so the
in-compute ensure_actor_eligible check soft-passes — it cannot
read the actor’s UserBinding / UserGdprState without a bound
view. This method closes that gap at the L2 boundary: when
authenticated_actor is Some, the service binds a fresh
InstanceView, runs the existing ensure_actor_eligible logic on
that injected actor, and REJECTS the action before submit if the
backing user is ErasurePending. The gate is SOUND — the actor it
gates on is the authenticated caller, the same single source of truth
the compute records. Its liveness has a production precondition: the
gate resolves actor → user through the UserBinding that
RegisterActor writes onto
the actor entity at registration time — an actor with no binding has
no resolvable user scope and soft-passes (system / anonymous actors).
For an actor registered through RegisterActor,
GdprEraseUser transitions
the user’s UserGdprState to ErasurePending with a blind write
(valid on the viewless compute path), so once erasure is requested
this gate rejects the user’s subsequent actions before submit (never
reaches the WAL), as this method’s production-path test demonstrates
end-to-end.
§Errors
DispatchError::ErasurePending— the L2 gate rejected the action (backing user inGdprStatus::ErasurePending).DispatchError::UnboundUserLifecycle— the actor’sUserBindingresolves to a user with noUserGdprState(fail-closed admission for an unregistered binding target).DispatchError::ProbeViewCorrupt— the gate probe read corrupt view bytes; fail closed.DispatchError::Kernel— kernel-side error fromsubmit(InstanceNotFoundifinstanceis not live). Capability denial happens insidestepand is reflected in the returned report’seffects_deniedcount rather than as anErr.
Sourcepub fn export_wal(self) -> Option<Wal>
pub fn export_wal(self) -> Option<Wal>
Drain the kernel’s internal WAL (consumes the service so the kernel cannot continue stepping after export).