Skip to main content

ikigai_script/
lib.rs

1//! **Scripts as resources in any ikigai host.** A script is not a feature beside the
2//! resources; it IS one, named `urn:script:{name}`, and every way of reaching it (a REST
3//! door, the REPL, an MCP agent, a timer, a panel) is a door onto the same names.
4//!
5//! ```text
6//! urn:script:{name}                    Source Exists Sink Delete  the script: fetch WITHOUT running, publish, retire
7//! urn:script:{name}:version:{digest}   Source Exists              one immutable, content-addressed version
8//! urn:script:{name}:compiled           Source                     the head prepared for its evaluator (cached)
9//! urn:script:{name}:result             Source                     run it as a READ: its answer
10//! urn:script:{name}:runs               Sink                       run it for EFFECTS: answers the run's IRI
11//! urn:script:{name}:run:{id}           Source Exists              one recorded run
12//! urn:script:eval                      Sink                       run supplied code under the caller's own authority
13//! urn:script:catalog                   Source                     every script the caller may read
14//! ```
15//!
16//! # A host library
17//!
18//! This crate binds no binary and mounts nowhere by itself. A host (`ikigai-embedded`,
19//! gonk, the CMS) mounts [`space`] beside the evaluator it offers and decides the three
20//! things only a host can — see [`SpaceConfig`]:
21//!
22//! - **where scripts live**: a [`Backend`] ([`MemoryBackend`], or [`DirBackend`] for plain
23//!   files under a directory the host chooses);
24//! - **the ceiling** each script may run under ([`authority::CeilingPolicy`], from the
25//!   host's `<config home>/script-authority/{name}`);
26//! - **who a request comes from**: the principal its door mints into the request's
27//!   capability (`Capability::with_principal`, `urn:cap:principal:<iri>`), read back with
28//!   [`authority::principal_of`], recorded on every publish and run and never taken from
29//!   the caller.
30//!
31//! ```
32//! use ikigai_core::{Capability, Fallback, Kernel, Space};
33//! use ikigai_script::{authority, space, MemoryBackend, SpaceConfig};
34//! use std::sync::Arc;
35//!
36//! let scripts = space(SpaceConfig::new(
37//!     Arc::new(MemoryBackend::new()),
38//!     // Fail closed: a script the host has not given a ceiling may touch nothing.
39//!     authority::same_for_all(authority::Ceiling::nothing()),
40//! ));
41//! // Beside the evaluator the scripts are written for (`ikigai_lisp::space()`):
42//! let root = Fallback::new(vec![Arc::new(scripts) as Arc<dyn Space> /*, lisp, … */]);
43//! let kernel = Kernel::new(Arc::new(root));
44//! # let _ = (kernel, Capability::root());
45//! ```
46//!
47//! # The authority rule
48//!
49//! Three authorities, never merged: **publish** (`urn:cap:script:write:{name}`, and
50//! `…:delete:{name}` to retire), **run** (`urn:cap:script:run:{name}`, or
51//! [`authority::CAP_RUN_PUBLIC`] for a public script), and **runs as**:
52//!
53//! ```text
54//! runner's capability, attenuated to
55//!   { declared } ∩ { what the publisher held at publish } ∩ { the host's ceiling }
56//!   + every exclusion the publisher or the ceiling carries
57//! ```
58//!
59//! **No elevation**: a publish declaring a scope its publisher does not hold is refused,
60//! a run never holds anything its runner does not, and the only way a run's authority is
61//! made is [`Invocation::issue_attenuated`](ikigai_core::Invocation::issue_attenuated) —
62//! there is no form that widens. See [`authority`].
63//!
64//! # Languages are resources
65//!
66//! A run is a sub-request to the language's evaluator, so this crate links no interpreter:
67//!
68//! - **Lisp** reaches `urn:lisp:eval`, and the evaluator's own capability (`urn:cap:lisp`) is
69//!   part of what every Lisp script declares: evaluating code is itself authority.
70//! - **SPARQL** reaches the host's store through its graph-scoped doors
71//!   ([`sparql::SparqlDoor`]). A query's form, its declared and typed parameters, and the
72//!   authority it needs (the store's per-graph grants) are all read from the parsed text,
73//!   and parameter values are bound into the parsed algebra, never spliced. Each published
74//!   query is its own catalog entry with its parameters as arguments. See [`sparql`].
75//! - **Plans** (`ik:Process` graphs) reach the host's `urn:plan:eval`. A plan is validated
76//!   by `urn:plan:validate` before it is stored, its authority is DERIVED from its steps'
77//!   contracts by `urn:plan:requires`, and it is a read or a write by its steps' verbs. Each
78//!   published plan is its own catalog entry with its parameters as arguments. See [`plan`].
79//!
80//! **Preference order when assembling anything: a query, then a plan, then Lisp.** The first
81//! two derive their authority and are checked before they run; use Lisp for control flow a
82//! plan cannot express, and preferably as one step inside a plan.
83
84#![deny(missing_docs)]
85
86pub mod authority;
87pub mod backend;
88pub mod endpoints;
89pub mod graph;
90pub mod limits;
91pub mod model;
92pub mod name;
93pub mod plan;
94pub mod sparql;
95
96pub use backend::{Backend, DirBackend, MemoryBackend};
97pub use endpoints::{
98    space, Catalog, CatalogEntry, ChangeHook, LastRun, Prepared, ScriptSpace, SpaceConfig,
99};
100pub use model::{Head, Language, Outcome, Run, State, Version};
101
102/// The README's example, compiled (never run: it opens a directory) so it cannot rot.
103#[cfg(doctest)]
104#[doc = include_str!("../README.md")]
105pub struct ReadmeDoctests;