1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
//! **A layer host.** A layer is one independent shared space between participants, named
//! `urn:layer:{layer}`: its definition (a file in the host's configuration) chooses an
//! ACCESS MODEL, names its closed SHACL admission shapes and its egress context, and its
//! grants are capabilities named for it (`urn:cap:layer:{verb}:{layer}`). It has its own
//! gapless feed. Adding a layer whose model exists is configuration, never code.
//!
//! A collaboration is a MANIFEST (`urn:collab:{name}`): which layers it mounts and which
//! participant holds which verbs on each. The door's enrollment (cli's `grants.json` and
//! `clients.json`) is generated from the manifests ([`grants`], and the `ikigai-layers
//! grants <config dir>` binary), so the manifest is the one thing an operator edits.
//!
//! This version builds two access models: `channel` (append-only entries, one graph per
//! author) and `tuplespace` (offers taken atomically, reports addressed to their offerer,
//! with receipts; see [`tuplespace`]).
//!
//! On those two, a GATEWAY between kernels is configuration plus a reference participant
//! ([`gateway`]): an ask layer per asker carries requests and their answers (a representation,
//! or a typed error), and an event channel per resource owner carries invalidations.
//!
//! Every answer a participant reads also has a JSON face ([`json`]), so a participant with
//! only an HTTP client and a JSON parser can take part: a graph as the layer's egress context,
//! pruned and compacted, always one top-level `@graph` array (`as=application/ld+json`), and a
//! list or a write's answer as plain JSON (`as=application/json`).
//!
//! ```text
//! urn:layer:{layer} Source the definition (ik:Layer) read:{layer}
//! urn:layer:{layer}:shapes Source the closed admission shapes read:{layer}
//! urn:layer:{layer}:context Source the egress context (JSON-LD) read:{layer}
//! urn:layer:{layer}:feed Source events after= (wait= ≤ 60 s) feed:{layer}
//! urn:layer:{layer}:say Sink append one entry, answer its IRI say:{layer}
//! urn:layer:{layer}:entries Source entries after= from= type= read:{layer}
//! urn:layer:{layer}:entry:{id} Source one entry (never changes) read:{layer}
//! urn:layer:{layer}:graph:{slug} Source one author's entries read:{layer}
//! urn:layer:{layer}:latest Source newest entry about= not superseded read:{layer}
//! urn:layer:{layer}:tuples Sink out: drop one tuple, its subject out:{layer}
//! Source rd: pending tuples, or receipts read:{layer}
//! Delete take: one tuple, atomically take:{layer}
//! urn:layer:{layer}:receipt:{id} Source one take (never changes) read:{layer}
//! urn:layers:feed Source one wait over several feeds feed:{each layer}
//! urn:layers:participants Source who each principal is layers:read:participants
//! urn:layers:me Source who the door says the caller is collab:read:* (any grant)
//! urn:collab:{name} Source a manifest collab:read:{name}
//! urn:collab:{name}:participants Source who takes part in it collab:read:{name}
//! ```
//!
//! (Capabilities abbreviated: `read:{layer}` is `urn:cap:layer:read:{layer}`.)
//!
//! # Two builds: the host, and a participant
//!
//! The `host` feature, on by default, is the layer host: [`Host`], the layer space a door
//! binds, the configuration, both access models, the feeds, the JSON faces and the enrollment
//! generator. A PARTICIPANT, a tool that only talks to a layer door (the [`gateway`]'s `Asker`
//! and `Resolver` over QUIC, say), takes the crate with `default-features = false` and gets the
//! modules that need no host: [`gateway`], [`name`], [`rdf`], [`id`] and [`time`]. That build
//! links no store, no space, no validator and no JSON-LD processor, so no oxigraph and no
//! RocksDB (ledger #1403).
//!
//! ```toml
//! [dependencies]
//! ikigai-layers = { version = "0.2.10", default-features = false }
//! ```
//!
//! # A host library
//!
//! No binary serves anything: a host (ikigai-cli's `serve`, ledger #1188) opens a [`Host`]
//! over its configuration and state directories and binds [`space`] with a clock (every
//! entry is stamped with the kernel's time). The door mints each connection's principal;
//! `say`, `out` and `take` refuse a request that carries none.
//!
//! **The layer space is all a door binds.** Admission validates with ikigai-shacl, which
//! this crate links and calls itself, so the door's kernel needs no `urn:shacl:validate`,
//! and binding one beside would offer it to every participant: it declares no capability
//! (ledger #1266). **One host per state directory**: [`Host::open`] locks it, and refuses
//! while another host holds it (ledger #1267).
//!
//! ```
//! # #[cfg(feature = "host")]
//! # fn main() {
//! use std::sync::Arc;
//! use ikigai_core::{Kernel, SystemClock};
//!
//! # let dir = std::env::temp_dir().join(format!("ikigai-layers-doc-{}", std::process::id()));
//! # let (config, state) = (dir.join("config"), dir.join("state"));
//! # std::fs::create_dir_all(&config).unwrap();
//! let host = ikigai_layers::Host::open(&config, &state).expect("a configuration that checks");
//! let kernel = Kernel::new(Arc::new(ikigai_layers::space(&host))).with_clock(Arc::new(SystemClock));
//! // A second host over the same state directory is refused while this one lives.
//! assert!(ikigai_layers::Host::open(&config, &state).is_err());
//! # drop(kernel);
//! # drop(host);
//! # std::fs::remove_dir_all(&dir).unwrap();
//! # }
//! # #[cfg(not(feature = "host"))]
//! # fn main() {}
//! ```
//!
//! # Isolation
//!
//! Separate channels, separate workflows, no overlap unless decided (Brian, 2026-10-10). A
//! layer belongs to ONE collaboration unless its definition says `shared = [..]` naming
//! every collaboration that mounts it; a manifest that breaks this is refused when the host
//! opens and when the generator runs, so no generated grant reaches another
//! collaboration's layer. See [`config::load_collabs`].
//!
//! # What this crate never writes
//!
//! `ik:layer` (lowercase) is a dry run's chain cell with `rdfs:domain ik:Consultation`;
//! writing it would type the subject a consultation. A layer is an `ik:Layer`, and the
//! layer an entry belongs to is the `{layer}` segment of its own IRI.
// The crate's documentation describes the host, and links its items. Built without `host`
// (a participant), those items do not exist, so the links have nowhere to go: allowed in that
// build only. The default build, which `cargo doc` in the shared CI runs with `-D warnings`,
// still refuses every broken link, in every module.
pub use ;
pub use space;
pub use Host;
/// The README's examples, compiled so they cannot rot. They open a host, so they run with it.
;