Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ Each domain is its own package, frozen at a per-file version. A capsule imports
| `host/elicit@1.0.0.wit` | `astrid:elicit@1.0.0` | Interactive user input during install/upgrade lifecycle. |
| `host/approval@1.0.0.wit` | `astrid:approval@1.0.0` | Human-in-the-loop approval gate for sensitive actions. |
| `host/identity@1.0.0.wit` | `astrid:identity@1.0.0` | Multi-platform identity resolve and link. |
| `host/compute@1.0.0.wit` | `astrid:compute@1.0.0` | Draft principal-scoped core-Wasm compute groups, bounded shared memory, jobs, cancellation and accounting. Requires a live owner-UID/capsule/package-generation provision and finite deployment policy; see [compute RFC #42](https://github.com/astrid-runtime/rfcs/pull/42). |
| `host/guest@1.0.0.wit` | `astrid:guest@1.0.0` | Guest export contract — `astrid-hook-trigger`, `run`, `astrid-install`, `astrid-upgrade`. Each entry point lives in its own world (`interceptor`, `background`, `installable`, `upgradable`) so capsules `include` only what they implement. |

A capsule's world declares only the imports it uses plus the guest-export worlds it actually implements:
Expand Down
201 changes: 201 additions & 0 deletions host/compute@1.0.0.wit
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
/// Principal-scoped parallel execution of capsule-authored core-Wasm workers.
///
/// DRAFT. This contract is developed on an unmerged branch. If merged, this
/// package version follows the repository immutability policy. The host is
/// algorithm-blind: the capsule supplies signed `compute-worker` bytes;
/// Astrid validates, schedules, meters, cancels, and accounts for them.
///
/// Workers import one bounded shared memory32 or memory64 and may use ABI-1
/// read-only imports for their authenticated, ordered immutable assets. Bulk
/// data and synchronization stay in that memory; this is the audited control
/// plane. A signed declaration identifies candidate bytes, not execution
/// authority. The host independently provisions the current principal UID,
/// capsule and exact published generation, including worker/asset digests, and
/// rechecks the live registry at activation and open.

package astrid:compute@1.0.0;

interface host {
use astrid:io/poll@1.0.0.{pollable};

/// Stable error classification. Strings are diagnostics only.
variant error-code {
/// No current authenticated owner/capsule/generation provision, or its
/// publication was replaced or revoked in the live registry.
capability-denied,
/// A count, range, memory shape, or other request field is invalid.
invalid-input(string),
/// The id is unknown or does not name a `compute-worker` component.
no-such-worker,
/// Hash, imports, exports, memory type, or ABI version is invalid.
worker-invalid(string),
/// Effective principal/operator worker or memory policy denied admission.
quota,
/// A targeted worker already has one pending/running job, or an
/// untargeted dispatch meets a full bounded worker queue.
busy,
/// Operation raced group, principal, capsule, or kernel cancellation.
cancelled,
/// Worker compile, instantiate, trap, fuel, or forced-interrupt failure.
worker-failed(string),
/// The resource is already terminal or closed.
closed,
/// A control-plane read or write exceeded the per-call transfer limit.
too-large,
/// Non-classified host error. Detail is best-effort.
unknown(string),
}

/// Whether the runtime may execute more than one worker concurrently.
enum execution-mode {
/// Resolve exactly one worker and dispatch jobs FIFO.
deterministic,
/// Use the resolved parallelism.
parallel,
}

/// How a requested worker count is resolved.
variant parallelism {
/// Useful host parallelism, bounded by admitted operator policy/reservations.
auto,
/// Zero is invalid. In parallel mode, admit exactly this positive count
/// or fail with `quota`.
/// Deterministic mode selects one worker regardless of a positive count.
exact(u32),
/// Zero is invalid. Admit at least one and no more than this count.
at-most(u32),
}

/// Request to open one compute group over one declared worker artifact.
record group-request {
/// `[[component]] id` whose type is `compute-worker`.
worker: string,
mode: execution-mode,
parallelism: parallelism,
/// Initial 64-KiB shared-memory pages. Must be non-zero.
initial-memory-pages: u32,
/// Maximum pages; zero requests automatic resolution against the worker
/// maximum and remaining policy capacity. A nonzero value must be
/// >= initial; the resolved shared memory always has a finite maximum.
maximum-memory-pages: u32,
}

/// Opaque pointer to capsule-defined bytes in the group's shared memory.
record work-descriptor {
/// Byte offset. Must be >= 64 (the advisory ABI header occupies 0..64).
offset: u64,
/// Byte length. `offset + length` is checked for overflow and bounds.
length: u64,
/// Application-defined value passed through without interpretation.
tag: u64,
/// `none` schedules the next available worker; `some` pins by index.
worker-index: option<u32>,
/// Optional self-limit. The daemon resolves `none` to a finite share
/// derived from effective principal fuel-rate policy, the admitted
/// job maximum and worker count, further narrowed atomically to currently
/// unspent/unreserved capacity. Zero capacity and `some(0)` deny admission.
/// Explicit positive values are admitted exactly or denied; they cannot
/// widen policy. Worker startup also requires finite fuel and a timeout.
fuel: option<u64>,
}

enum job-state {
queued,
running,
completed,
cancelled,
failed,
}

/// Non-blocking snapshot of one job.
record job-status {
state: job-state,
worker-index: option<u32>,
}

/// Terminal result of a worker invocation.
record job-result {
state: job-state,
worker-index: u32,
/// Capsule-defined `astrid_compute_run` return code.
worker-status: s32,
fuel-consumed: u64,
elapsed-ns: u64,
}

/// Group and principal-attributed counters. Values are host-stamped, but
/// owner UID, generation, digests and assets require a separate host audit.
record accounting {
workers-reserved: u32,
memory-bytes-current: u64,
memory-bytes-peak: u64,
jobs-submitted: u64,
jobs-completed: u64,
jobs-cancelled: u64,
jobs-failed: u64,
fuel-consumed: u64,
}

/// Non-blocking snapshot of one compute group.
record group-info {
worker: string,
mode: execution-mode,
parallelism: u32,
memory-pages: u32,
maximum-memory-pages: u32,
queued-jobs: u32,
running-jobs: u32,
usage: accounting,
}

/// Observer handle for one submitted invocation.
///
/// Dropping this handle does not cancel work. The owning group controls the
/// worker lifetime and cancels every queued/running job when dropped.
resource job {
/// Current state without blocking.
status: func() -> result<job-status, error-code>;

/// Pollable that becomes ready when the job reaches a terminal state.
subscribe: func() -> pollable;

/// Wait for completion or a typed terminal failure.
join: func() -> result<job-result, error-code>;

/// Request cancellation. Idempotent.
cancel: func() -> result<_, error-code>;
}

/// Worker instances plus the shared memory they import.
resource compute-group {
/// Current immutable configuration and mutable counters.
info: func() -> result<group-info, error-code>;

/// Copy bytes out of retained shared memory (maximum 1 MiB/call).
/// Cancelled groups may permit reads until the resource is retired.
read: func(offset: u64, length: u32) -> result<list<u8>, error-code>;

/// Copy bytes into retained shared memory (maximum 1 MiB/call).
/// Cancellation alone does not retire the memory; retirement closes access.
write: func(offset: u64, data: list<u8>) -> result<_, error-code>;

/// Grow within the admitted maximum, returning the previous page count
/// and updating accounting/header. Closed groups reject growth.
grow: func(delta-pages: u32) -> result<u32, error-code>;

/// Queue one worker invocation and return immediately.
submit: func(descriptor: work-descriptor) -> result<job, error-code>;

/// Cancel all queued/running work and reject new submissions/growth.
/// This is not evidence that native workers exited or RAM was released.
cancel: func() -> result<_, error-code>;
}

/// Open requires a live, independently authenticated package provision bound
/// to immutable principal UID, capsule and exact publication generation,
/// checked against the live registry. A declaration alone grants nothing.
/// A failed call returns no usable group. Interrupted startup may leave a
/// worker settling asynchronously; its memory and reservations remain
/// charged until the worker actually exits and cleanup releases them.
open: func(request: group-request) -> result<compute-group, error-code>;
}
Loading