Docs/Runtime

Use one owner for each runtime job.

Session owns lifecycle. Tasks own cooperative work. Network owns byte transport. Telemetry observes completed work without replacing its result.

Focused headersBounded resourcesTyped outcomes

Choose the narrowest public entry

TaskHeaderOwner
Open, reuse, and close a runtime<rund/session.hpp>rund::Session, one-shot rund::run, and telemetry configuration.
Spawn and await cooperative work<rund/task.hpp>rund::task tasks, groups, channels, yield, sleep, and cancellation.
Admit files, environment, random, and logical time<rund/host.hpp>rund::host host-observation boundary.
Move bytes over sockets<rund/net.hpp>rund::net socket ownership, readiness, stream/datagram I/O, and bounded servers.
Account retained storage<rund/storage.hpp>rund::storage hierarchical budget admission.
Optional deterministic placement<cluster/cluster.hpp>rund::cluster shard placement and retry identity.

Every entry still links through the single runD::sdk CMake target.

Start with one-shot Session work

Use rund::run for one bounded callback. It opens the Session, runs the callback, drains scheduled work, closes, and returns one result.

runtime.cpp · checked installed example
#include <rund/session.hpp>

int main() {
  const rund::Session::Result result =
      rund::run(rund::SessionConfig{.workers = 1u}, [] {});
  return result.exit_code();
}

Open once for repeated work

reusable Session · contextual fragment
rund::Session session;
auto opened = session.open(config);
if (!opened) return opened.exit_code();

for (const auto& tick : ticks) {
  auto completed = session.scope([&] { simulate(tick); });
  if (!completed) { operation = completed.exit_code(); break; }
}

auto closed = session.close();
return operation == 0 ? closed.exit_code() : operation;

Reuse one Session for repeated ticks. Ordinary code calls blocking close() once; use drain() only when you deliberately need to observe the Draining state.

Spawn cooperative work inside a Session

Include <rund/task.hpp> for bounded work that shares the active Session scheduler. Keep the returned Handle, check admission, and join it before captured state goes out of scope.

task.cpp · checked installed example
#include <rund/session.hpp>
#include <rund/task.hpp>

int main() {
  int value = 0;
  rund::task::Status task_result = rund::task::Status::success();
  const rund::Session::Result hosted =
      rund::run(rund::SessionConfig{.workers = 1u}, [&] {
        const rund::task::Handle task =
            rund::task::spawn("set-value", [&] { value = 42; });
        if (!task) {
          task_result = rund::task::Status::fail(task.code());
          return;
        }
        task_result = rund::task::join(task);
      });
  if (!hosted) {
    return hosted.exit_code();
  }
  if (!task_result) {
    return task_result.exit_code();
  }
  return value == 42 ? 0 : 2;
}

Handle::code() is the admission failure; Status::code() is the joined task failure. A missing runtime requires an active Session scope. A capacity failure requires fewer live tasks or a larger caller-configured scheduler capacity. Neither failure means the callable ran.

Admit host input once

Include <rund/host.hpp> for environment, file, random, logical-clock, and timer operations. This example reads an environment value while the Session records the host observation.

host.cpp · checked installed example
#include <rund/host.hpp>
#include <rund/session.hpp>

#include <string>
#include <utility>

int main() {
  int operation = 0;
  std::string path{};
  const rund::Session::Result hosted =
      rund::run(rund::SessionConfig{.workers = 1u}, [&] {
        auto value = rund::host::env::get("PATH");
        if (!value) {
          operation = value.exit_code();
          return;
        }
        path = std::move(*value);
      });
  if (!hosted) {
    return hosted.exit_code();
  }
  if (operation != 0) {
    return operation;
  }
  (void)path;
  return 0;
}

A missing environment variable is a successful empty value; a failed Result means the operation itself could not be admitted or represented. Inspect its code() or error(), fix the input or resource condition, and rerun the containing scope.

One input authority Network or host bytes are not simulation state by themselves. Decode and validate them once, then admit canonical input through Replay.

Reserve storage before retaining bytes

Include <rund/storage.hpp> when several producers must share one hard in-process byte budget. Reserve a conservative allocated-byte ceiling, commit measured use, then refund when the retained data is released.

storage.cpp · checked installed example
#include <rund/storage.hpp>

int main() {
  rund::storage::Budget budget{1024u};
  if (!budget) {
    return budget.exit_code();
  }

  auto reservation = budget.reserve(256u);
  if (!reservation) {
    return reservation.exit_code();
  }
  const rund::storage::Status committed = reservation.commit(
      rund::storage::Usage{.physical_bytes = 128u, .allocated_bytes = 256u});
  if (!committed) {
    return committed.exit_code();
  }

  const rund::storage::Report used = budget.report();
  if (!used) {
    return used.exit_code();
  }
  if (used.allocated_bytes != 256u || used.available_bytes != 768u) {
    return 2;
  }

  const rund::storage::Status refunded = reservation.refund();
  return refunded.exit_code();
}

StorageCapacityExceeded means another live reservation or committed allocation owns the bytes; release it or change the owner-set budget before retrying. A commit failure means the supplied measured allocation exceeded its reservation or was otherwise invalid. The Reservation keeps the exactly-once refund obligation until refund or destruction.

Place a shard from an ordered candidate set

Include <cluster/cluster.hpp> only when the application needs optional deterministic placement or retry identity. The application supplies stable shard, candidate, and epoch identities; runD returns one typed decision.

cluster.cpp · checked installed example
#include <cluster/cluster.hpp>

#include <array>

int main() {
  const rund::cluster::ShardRef shard{
      rund::cluster::JobId{10u},
      rund::cluster::ShardId{20u},
  };
  constexpr std::array<rund::cluster::NodeId, 2u> candidates{
      rund::cluster::NodeId{40u},
      rund::cluster::NodeId{50u},
  };
  const rund::cluster::PlacementResult placed =
      rund::cluster::place_shard(rund::cluster::PlacementRequest{
          shard, candidates, rund::cluster::PlacementEpoch{7u}});
  if (!placed) {
    return placed.exit_code();
  }
  return placed.placement.node == candidates.front() ? 0 : 2;
}

PlacementCode::ShardRequired means the shard identity is incomplete; NodeRequired means the ordered candidate set has no valid node. Correct those inputs and evaluate again. Candidate order is part of the caller-supplied decision input.

Network owns transport, not protocol meaning

net::Socket is the move-only owner. SocketView is a borrowed, non-closing capability. Scalar stream and datagram operations are move-only awaitables; the application owns buffers and protocol interpretation.

network.cpp · checked compilation example
#include <rund/net.hpp>
#include <rund/task.hpp>

#include <cstddef>
#include <span>

rund::task::Task<void> Read(const rund::net::SocketView socket,
                            const std::span<std::byte> bytes,
                            rund::net::ReceiveResult &result) {
  result = co_await rund::net::receive(socket, bytes);
}

rund::task::Task<void> SendPacket(const rund::net::SocketView socket,
                                  const std::span<const std::byte> bytes,
                                  const rund::net::Address peer,
                                  rund::net::datagram::SendResult &result) {
  result = co_await rund::net::datagram::send(socket, bytes, peer);
}

Readiness sets, accept/connect, drains, frames, and servers add explicit budgets. Scope exit closes owning sockets; a borrowed view cannot close or outlive its owner.

Observe cost without changing the result

Bind one lvalue observer to SessionConfig. Basic telemetry reports stable identities, counters, hashes, and findings. Detail additionally measures phase timing.

telemetry.cpp · checked installed example
#include <rund/replay.hpp>

#include <cstdio>
#include <string_view>

int main() {
  auto observe = [](const rund::telemetry::Event &event) {
    rund::telemetry::describe(event, [](const std::string_view text) {
      std::printf("%.*s", static_cast<int>(text.size()), text.data());
    });
    std::putchar('\n');
  };

  rund::SessionConfig config{.workers = 1u};
  config.telemetry = rund::telemetry::bind(observe);
  rund::Session session{};
  const rund::Session::Status opened = session.open(config);
  if (!opened) {
    return opened.exit_code();
  }

  const rund::replay::Live result =
      rund::replay::live(session, [](rund::replay::Context &) {});
  const rund::Session::Status closed = session.close();
  if (!result) {
    return result.exit_code();
  }
  return closed.exit_code();
}

The observer must outlive terminal operations and Session::close(). A finding suggests an action; it never replaces the operation's typed outcome.

Next SDK guideChoose the numeric surface that owns your state