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**. Each grant names one
52//! script or a namespace of them (`urn:cap:script:{act}:{namespace}-*`,
53//! [`authority::cap_namespace`]), and [`authority::holds`] decides. A run runs as:
54//!
55//! ```text
56//! runner's capability, attenuated to
57//!   { declared } ∩ { what the publisher held at publish } ∩ { the host's ceiling }
58//!   + every exclusion the publisher or the ceiling carries
59//! ```
60//!
61//! **No elevation**: a publish declaring a scope its publisher does not hold is refused,
62//! a run never holds anything its runner does not, and the only way a run's authority is
63//! made is [`Invocation::issue_attenuated`](ikigai_core::Invocation::issue_attenuated) —
64//! there is no form that widens. See [`authority`].
65//!
66//! # Languages are resources
67//!
68//! A run is a sub-request to the language's evaluator, so this crate links no interpreter:
69//!
70//! - **Lisp** reaches `urn:lisp:eval`, and the evaluator's own capability (`urn:cap:lisp`) is
71//!   part of what every Lisp script declares: evaluating code is itself authority.
72//! - **SPARQL** reaches the host's store through its graph-scoped doors
73//!   ([`sparql::SparqlDoor`]). A query's form, its declared and typed parameters, and the
74//!   authority it needs (the store's per-graph grants) are all read from the parsed text,
75//!   and parameter values are bound into the parsed algebra, never spliced. Each published
76//!   query is its own catalog entry with its parameters as arguments. See [`sparql`].
77//! - **Plans** (`ik:Process` graphs) reach the host's `urn:plan:eval`. A plan is validated
78//!   by `urn:plan:validate` before it is stored, its authority is DERIVED from its steps'
79//!   contracts by `urn:plan:requires`, and it is a read or a write by its steps' verbs. Each
80//!   published plan is its own catalog entry with its parameters as arguments. See [`plan`].
81//!
82//! **Preference order when assembling anything: a query, then a plan, then Lisp.** The first
83//! two derive their authority and are checked before they run; use Lisp for control flow a
84//! plan cannot express, and preferably as one step inside a plan.
85
86#![deny(missing_docs)]
87
88pub mod authority;
89pub mod backend;
90pub mod endpoints;
91pub mod graph;
92pub mod limits;
93pub mod model;
94pub mod name;
95pub mod plan;
96pub mod sparql;
97
98pub use backend::{Backend, DirBackend, MemoryBackend};
99pub use endpoints::{
100    space, Catalog, CatalogEntry, ChangeHook, LastRun, Prepared, ScriptSpace, SpaceConfig,
101    TRANSPORT_ARGUMENTS,
102};
103pub use model::{Head, Language, Outcome, Run, State, Version};
104
105/// The README's example, compiled (never run: it opens a directory) so it cannot rot.
106#[cfg(doctest)]
107#[doc = include_str!("../README.md")]
108pub struct ReadmeDoctests;