Skip to main content

RuntimeService

Struct RuntimeService 

Source
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

Source

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.

Source

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.

Source

pub fn create_instance(&mut self, config: InstanceConfig) -> InstanceId

Create a fresh kernel instance and return its InstanceId.

Source

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) ∩ capsPrincipal::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 in GdprStatus::ErasurePending).
  • DispatchError::UnboundUserLifecycle — the actor’s UserBinding resolves to a user with no UserGdprState (fail-closed admission for an unregistered binding target).
  • DispatchError::ProbeViewCorrupt — the gate probe read corrupt view bytes; fail closed.
  • DispatchError::Kernel — kernel-side error from submit (InstanceNotFound if instance is not live). Capability denial happens inside step and is reflected in the returned report’s effects_denied count rather than as an Err.
Source

pub fn export_wal(self) -> Option<Wal>

Drain the kernel’s internal WAL (consumes the service so the kernel cannot continue stepping after export).

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.