ontolith.plugins¶
ontolith.plugins ¶
Plugin capability isolation (ADR-0015).
Public surface for plugin authors and hosts:
- PluginManifest/PluginCapabilities/PluginKind — what a plugin declares
- ReadOnlyView/WriteView — capability-scoped facades a plugin receives
- PluginRegistry/LoadedPlugin — discovery and least-privilege loading
- Importer/Exporter/Reasoner/Validator/Connector — protocol interfaces a
plugin implements (SPEC §13.2)
- ValidatorKbView — the structural read view Validator.validate()'s kb
parameter accepts (KI-042, ADR-0029); useful for precisely typing a
custom Validator's own validate() signature
PluginKind
module-attribute
¶
Plugin kinds covered by capability isolation.
Deliberately excludes StorageBackend/AuthProvider/PolicyStrategy/Embedder
plugins: those are infrastructure-extension points the framework calls INTO
(e.g. a StorageBackend plugin IS the persistence substrate underneath
WriteView, not a principal-scoped actor calling through it) — they don't fit
the capability-scoped-view model this module implements. See ADR-0015
"Plugin kinds in scope" and ADR-0020 (Embedder specifically: its Protocol
takes no kb view parameter at all, so it structurally cannot participate
in this module's ReadOnlyView/WriteView dispatch; it lives in
core/embedder.py, injected into Ontology directly like Clock/IdProvider).
PluginCapabilities ¶
Bases: BaseModel
Capabilities a plugin requests.
Attributes:
| Name | Type | Description |
|---|---|---|
storage |
Literal['read', 'propose', 'write']
|
Requested ceiling on the read/propose/write lattice (identity.principal._CAPABILITY_ORDER). Enforced by PluginRegistry — the plugin's effective capability is min(this, the capability granted at registration), further capped to "read" for read-only plugin kinds regardless of what is requested here. |
network |
bool
|
Declares intent to make network calls. Enforced via OS
syscall denial when |
filesystem |
bool
|
Declares intent to touch the filesystem. Same
enforcement story as |
PluginManifest ¶
Bases: BaseModel
A plugin's declared identity and capability request.
Attributes:
| Name | Type | Description |
|---|---|---|
name |
str
|
Plugin identifier. Becomes the id of the service-kind Principal PluginRegistry creates/reuses for this plugin. |
version |
str
|
Plugin version string. |
kind |
PluginKind
|
What kind of plugin this is — determines the ceiling on effective storage capability (read-only kinds are capped at "read" regardless of requested/granted capability) and which view type (ReadOnlyView vs WriteView) the plugin receives. |
capabilities |
PluginCapabilities
|
Requested capabilities (see PluginCapabilities). |
Connector ¶
Bases: Protocol
Syncs KB state with an external system through a governed WriteView.
Exporter ¶
Bases: Protocol
Exports KB data to an external target through a ReadOnlyView.
Importer ¶
Reasoner ¶
Bases: Protocol
Derives new assertions from existing KB state through a WriteView.
Derived assertions MUST be submitted via kb.propose()/kb.propose_ref() — WriteView has no other write path. This is what SPEC §13.2's "reasoner- derived assertions MUST enter through the proposal path" means in practice: there is no bypass to forget to block.
Validator ¶
Bases: Protocol
Validates an assertion against custom rules through a read-only KB view.
assertion is the thing under validation when a Validator is invoked
per-assertion (e.g. via Ontology.validators — KI-042, ADR-0029). A
Validator invoked for whole-entity completeness instead (e.g. via
Ontology.completeness_validators) receives an assertion that is
only a subject stand-in — implementations with that shape (like
RequiredFieldsValidator) should read assertion.subject and re-query
kb for current state, and must not rely on assertion's other
fields describing anything currently true or just-committed.
validate ¶
ValidatorKbView ¶
Bases: Protocol
Structural read view a Validator may query (KI-042, ADR-0029).
Deliberately narrower than the concrete ReadOnlyView class this
protocol is otherwise modeled on: Ontology itself (ontology.py)
already exposes this exact method shape and is what gets passed
directly as kb when a Validator registered via Ontology's own
validators/completeness_validators constructor parameters runs
synchronously inside the write path (as opposed to a Validator loaded
through PluginRegistry.register(), which still receives a real,
capability-scoped ReadOnlyView) — this path is always trusted and
unsandboxed, regardless of ADR-0051: never pass a PluginRegistry-
loaded plugin's LoadedPlugin.instance (an IsolatedPluginProxy when
isolate=True) into validators/completeness_validators — Ontology
itself isn't a ReadOnlyView/picklable, so it can't cross the sandbox
boundary at all; call loaded.instance.validate(assertion, loaded.view)
directly instead, the same way every other plugin kind is called
through PluginRegistry. Importing Ontology here to spell
that out as a second concrete type would be circular — ontology.py
needs Validator's type for its own constructor parameters, and this
module already imports ReadOnlyView/WriteView from
plugins/views.py, which itself imports Ontology. This minimal
Protocol is the common shape both ReadOnlyView and Ontology
already satisfy structurally, with no inheritance or import required —
the same pattern govern/policy.py's KbView uses for the same
reason (KI-017, ADR-0025).
LoadedPlugin
dataclass
¶
A discovered plugin bound to a capability-scoped view.
Attributes:
| Name | Type | Description |
|---|---|---|
manifest |
PluginManifest
|
The plugin's declared manifest. |
principal_id |
str
|
The service-Principal id this plugin acts as. |
view |
ReadOnlyView
|
ReadOnlyView or WriteView scoped to principal_id, per the plugin's effective capability. |
instance |
object
|
The plugin's one protocol entrypoint, callable the same
way the real object would be ( |
PluginRegistry ¶
Discovers plugins via entry points and loads them with least privilege.
Source code in src/ontolith/plugins/registry.py
register ¶
register(
entry_point_name: str,
*,
author: str,
granted_capability: str = "propose",
isolate: bool = True,
require_enforcement: bool = False,
) -> LoadedPlugin
Discover, load, and sandbox a plugin by entry-point name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
entry_point_name
|
str
|
Name of the entry point under the "ontolith.plugins" group. |
required |
author
|
str
|
Principal ID performing the registration — must hold
|
required |
granted_capability
|
str
|
Ceiling on what storage capability this plugin may be granted, regardless of what its manifest requests. Defaults to "propose" (least privilege) — further capped to "read" for read-only plugin kinds. |
'propose'
|
isolate
|
bool
|
Run the plugin's protocol entrypoint in a sandboxed
child process (ADR-0051), not this process. Defaults to
|
True
|
require_enforcement
|
bool
|
Refuse to register ( What this flag does NOT guarantee (round-3 review
finding, M4 Workstream 7): it protects against a
NON-ADVERSARIAL failure to install the filter — a
container's outer seccomp profile blocking it, an
unresolvable syscall name, missing libseccomp. It is NOT a
guarantee against a plugin whose own module-level code or
|
False
|
Returns:
| Type | Description |
|---|---|
LoadedPlugin
|
LoadedPlugin bound to a capability-scoped view. |
Raises:
| Type | Description |
|---|---|
AuthError
|
author is not a known principal |
CapabilityError
|
author lacks admin capability |
ValidationError
|
granted_capability is not a known capability level |
PluginError
|
entry point not found or ambiguous, manifest missing/invalid, a write-capable plugin's effective capability resolved to "read", the plugin's name is already occupied by an unrelated principal, or require_enforcement=True but OS-level enforcement can't be attempted for this registration at all |
Note
Logs a logging.WARNING (KI-014) if the plugin's manifest
declares capabilities.network/.filesystem and either
isolate=False was passed or no OS-level enforcement is
available for this platform/capability (ADR-0051) — the
declaration alone doesn't restrict anything in that case. Only
logged on successful registration (the plugin actually becomes
a standing in-process actor), not on a failed attempt.
Records a register_plugin AdminEvent on successful
registration (KI-060), same "only on success" timing as the
warning above. If this is the plugin's first registration
(no existing principal), _ensure_principal's own
create_principal(..., author=author) call also records a
separate create_principal event — two events for one
register() call in that case, one for each distinct action
that actually happened.
Source code in src/ontolith/plugins/registry.py
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 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 | |
ReadOnlyView ¶
Read-only facade over Ontology, scoped to a single principal id.
Source code in src/ontolith/plugins/views.py
get_entity ¶
schema ¶
The current schema for this view's namespace, or None if no
schema has been applied yet.
Added for the RDF/OWL exporter (SPEC §13.3), the first reference
plugin needing schema access rather than just entity/assertion
data — schema is namespace-scoped, non-sensitive metadata (no
principal-specific governance concern the way write access is),
so this is a plain passthrough with no capability narrowing.
Delegates to Ontology.schema(), not self._kb.backend directly —
every other method on this view delegates to an Ontology method
too (this module's own docstring calls it "a safe method subset of
Ontology"); reaching past that to the storage port would have been
the only exception.
Source code in src/ontolith/plugins/views.py
assertions ¶
assertions(
subject: str | None = None,
predicate: str | None = None,
status: str | None = "active",
) -> list[Assertion]
Query assertions.
Source code in src/ontolith/plugins/views.py
query ¶
WriteView ¶
Bases: ReadOnlyView
Governed-write facade over Ontology, scoped to a single principal id.
Adds create_entity/propose/propose_ref/retract — never the direct-write
bypass (assert_literal/assert_ref). author is always the view's own
principal id. propose/propose_ref/retract go through Ontology's SPEC §10
conflict routing and policy evaluation; create_entity is capability-gated
(Ontology.create_entity requires >= propose) but is NOT itself SPEC
§10 governed — entities are structural records, not assertions, and have
no proposal/review path in this codebase.
Source code in src/ontolith/plugins/views.py
create_entity ¶
Create a new entity, authored by this view's principal.
propose ¶
propose(
subject: str,
predicate: str,
value: str,
value_type: str,
*,
confidence: float | None = None,
source: str | None = None,
rationale: str | None = None,
) -> tuple[Proposal, Decision]
Submit a literal assertion through the proposal/policy path (SPEC §9).
Source code in src/ontolith/plugins/views.py
propose_ref ¶
propose_ref(
subject: str,
predicate: str,
target: str,
*,
confidence: float | None = None,
source: str | None = None,
rationale: str | None = None,
) -> tuple[Proposal, Decision]
Submit a reference (relation) assertion through the proposal/policy path (SPEC §9).
Source code in src/ontolith/plugins/views.py
retract ¶
Propose retraction of an assertion through the policy path (SPEC §9).