stigmem-plugin-source-attestation 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- stigmem_plugin_source_attestation/__init__.py +12 -0
- stigmem_plugin_source_attestation/config.py +48 -0
- stigmem_plugin_source_attestation/handlers.py +147 -0
- stigmem_plugin_source_attestation/manifest.py +43 -0
- stigmem_plugin_source_attestation-0.1.0.dist-info/METADATA +106 -0
- stigmem_plugin_source_attestation-0.1.0.dist-info/RECORD +8 -0
- stigmem_plugin_source_attestation-0.1.0.dist-info/WHEEL +4 -0
- stigmem_plugin_source_attestation-0.1.0.dist-info/entry_points.txt +2 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""Experimental source-attestation plugin scaffold."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from .config import SourceAttestationConfig
|
|
6
|
+
from .manifest import PLUGIN_NAME, plugin_manifest
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"PLUGIN_NAME",
|
|
10
|
+
"SourceAttestationConfig",
|
|
11
|
+
"plugin_manifest",
|
|
12
|
+
]
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""Configuration schema for the source-attestation plugin."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
from collections.abc import Mapping
|
|
7
|
+
|
|
8
|
+
from pydantic import BaseModel, model_validator
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class SourceAttestationConfig(BaseModel):
|
|
12
|
+
"""Operator-controlled gates for experimental source attestation."""
|
|
13
|
+
|
|
14
|
+
enabled: bool = False
|
|
15
|
+
enforce_assert_validation: bool = False
|
|
16
|
+
apply_recall_rank: bool = False
|
|
17
|
+
enforce_federation_inbound: bool = False
|
|
18
|
+
warn_only: bool = True
|
|
19
|
+
|
|
20
|
+
@model_validator(mode="after")
|
|
21
|
+
def _validate_warn_only_boundary(self) -> SourceAttestationConfig:
|
|
22
|
+
enforcing = self.enforce_assert_validation or self.enforce_federation_inbound
|
|
23
|
+
if enforcing and self.warn_only:
|
|
24
|
+
raise ValueError("warn_only must be false when enforcement gates are enabled")
|
|
25
|
+
return self
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def load_config_from_env(
|
|
29
|
+
environ: Mapping[str, str] | None = None,
|
|
30
|
+
) -> SourceAttestationConfig:
|
|
31
|
+
"""Load source-attestation plugin gates from environment variables."""
|
|
32
|
+
|
|
33
|
+
env = environ if environ is not None else os.environ
|
|
34
|
+
prefix = "STIGMEM_SOURCE_ATTESTATION_"
|
|
35
|
+
return SourceAttestationConfig(
|
|
36
|
+
enabled=_env_bool(env, f"{prefix}ENABLED"),
|
|
37
|
+
enforce_assert_validation=_env_bool(env, f"{prefix}ENFORCE_ASSERT_VALIDATION"),
|
|
38
|
+
apply_recall_rank=_env_bool(env, f"{prefix}APPLY_RECALL_RANK"),
|
|
39
|
+
enforce_federation_inbound=_env_bool(env, f"{prefix}ENFORCE_FEDERATION_INBOUND"),
|
|
40
|
+
warn_only=_env_bool(env, f"{prefix}WARN_ONLY", default=True),
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _env_bool(env: Mapping[str, str], name: str, *, default: bool = False) -> bool:
|
|
45
|
+
raw = env.get(name)
|
|
46
|
+
if raw is None:
|
|
47
|
+
return default
|
|
48
|
+
return raw.strip().lower() in {"1", "true", "yes", "on"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"""Hook handlers for the source-attestation plugin."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from pydantic import ValidationError
|
|
8
|
+
from stigmem_node.entity_normalizer import NormalizationError, normalize_entity_uri
|
|
9
|
+
from stigmem_node.plugins import Allow, Deny, PluginContext, PluginHealth, PluginHealthStatus
|
|
10
|
+
|
|
11
|
+
from .config import load_config_from_env
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def config_validate(_ctx: PluginContext, **_: Any) -> Allow | Deny:
|
|
15
|
+
"""Validate operator gates before the plugin is registered."""
|
|
16
|
+
|
|
17
|
+
try:
|
|
18
|
+
load_config_from_env()
|
|
19
|
+
except ValidationError as exc:
|
|
20
|
+
return Deny(f"invalid source-attestation plugin config: {exc}")
|
|
21
|
+
return Allow()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def pre_assert_validate(_ctx: PluginContext, **kwargs: Any) -> Allow | Deny:
|
|
25
|
+
"""Gate source/identity binding behind explicit plugin configuration.
|
|
26
|
+
|
|
27
|
+
Returns Allow() when the plugin is disabled or assertion enforcement is not
|
|
28
|
+
enabled. When both gates are enabled, the handler rejects declared source
|
|
29
|
+
values that do not match the authenticated principal or its delegated
|
|
30
|
+
source-entity list.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
config = load_config_from_env()
|
|
34
|
+
if not (config.enabled and config.enforce_assert_validation):
|
|
35
|
+
return Allow()
|
|
36
|
+
|
|
37
|
+
req = kwargs.get("req")
|
|
38
|
+
identity = kwargs.get("identity")
|
|
39
|
+
source = _normalized_or_none(getattr(req, "source", None))
|
|
40
|
+
authorized_sources = _authorized_source_entities(identity)
|
|
41
|
+
if source is not None and source in authorized_sources:
|
|
42
|
+
return Allow()
|
|
43
|
+
|
|
44
|
+
return Deny(
|
|
45
|
+
"source_attestation_failed: declared source does not match authenticated principal"
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def recall_rank(_ctx: PluginContext, scored_results: list[Any], **kwargs: Any) -> dict[str, float]:
|
|
50
|
+
"""Contribute source-trust score deltas only when explicitly enabled.
|
|
51
|
+
|
|
52
|
+
Returns an empty delta map when the plugin is disabled, recall ranking is
|
|
53
|
+
not enabled, or the active recall weights set source_trust to zero. Only
|
|
54
|
+
enabled deployments add source-trust contribution to caller-owned scoring.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
config = load_config_from_env()
|
|
58
|
+
if not (config.enabled and config.apply_recall_rank):
|
|
59
|
+
return {}
|
|
60
|
+
|
|
61
|
+
from stigmem_node.source_trust import compute_source_trust
|
|
62
|
+
|
|
63
|
+
identity = kwargs.get("identity")
|
|
64
|
+
weights = kwargs.get("weights")
|
|
65
|
+
source_weight = float(getattr(weights, "source_trust", 0.0))
|
|
66
|
+
if source_weight <= 0.0:
|
|
67
|
+
return {}
|
|
68
|
+
|
|
69
|
+
base_weight = (
|
|
70
|
+
float(getattr(weights, "lexical", 0.0))
|
|
71
|
+
+ float(getattr(weights, "semantic", 0.0))
|
|
72
|
+
+ float(getattr(weights, "graph", 0.0))
|
|
73
|
+
+ source_weight
|
|
74
|
+
+ float(getattr(weights, "recency", 0.0))
|
|
75
|
+
)
|
|
76
|
+
if base_weight <= 0.0:
|
|
77
|
+
base_weight = 1.0
|
|
78
|
+
|
|
79
|
+
deltas: dict[str, float] = {}
|
|
80
|
+
for scored in scored_results:
|
|
81
|
+
fact = getattr(scored, "fact", None)
|
|
82
|
+
fact_id = getattr(fact, "id", None)
|
|
83
|
+
source = getattr(fact, "source", None)
|
|
84
|
+
scope = getattr(fact, "scope", None)
|
|
85
|
+
confidence = float(getattr(fact, "confidence", 0.0))
|
|
86
|
+
if fact_id is None or source is None or scope is None:
|
|
87
|
+
continue
|
|
88
|
+
trust = compute_source_trust(source, scope, identity)
|
|
89
|
+
deltas[str(fact_id)] = (source_weight * trust / base_weight) * max(0.0, confidence)
|
|
90
|
+
|
|
91
|
+
return deltas
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def federation_inbound_validate(_ctx: PluginContext, **kwargs: Any) -> Allow | Deny:
|
|
95
|
+
"""Gate inbound source-attestation policy behind explicit plugin configuration.
|
|
96
|
+
|
|
97
|
+
Returns Allow() when the plugin is disabled or federation-inbound
|
|
98
|
+
enforcement is not enabled. When both gates are enabled, the handler
|
|
99
|
+
rejects federated facts whose normalized source does not match the peer
|
|
100
|
+
node identifier or capability-token subject.
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
config = load_config_from_env()
|
|
104
|
+
if not (config.enabled and config.enforce_federation_inbound):
|
|
105
|
+
return Allow()
|
|
106
|
+
|
|
107
|
+
fact = kwargs.get("fact") or {}
|
|
108
|
+
fact_source = _normalized_or_none(fact.get("source")) if isinstance(fact, dict) else None
|
|
109
|
+
peer = kwargs.get("peer") or {}
|
|
110
|
+
cap_token = kwargs.get("cap_token") or {}
|
|
111
|
+
expected_source = None
|
|
112
|
+
if isinstance(peer, dict):
|
|
113
|
+
expected_source = peer.get("node_id")
|
|
114
|
+
if expected_source is None and isinstance(cap_token, dict):
|
|
115
|
+
expected_source = cap_token.get("subject")
|
|
116
|
+
if fact_source is not None and fact_source == _normalized_or_none(expected_source):
|
|
117
|
+
return Allow()
|
|
118
|
+
|
|
119
|
+
return Deny("source_attestation_failed: federated fact source does not match sender")
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def health_check(_ctx: PluginContext) -> PluginHealth:
|
|
123
|
+
"""Report scaffold health for registry lifecycle tests."""
|
|
124
|
+
|
|
125
|
+
return PluginHealth(
|
|
126
|
+
status=PluginHealthStatus.HEALTHY,
|
|
127
|
+
message="source attestation plugin scaffold registered",
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _authorized_source_entities(identity: Any) -> set[str]:
|
|
132
|
+
source_entities = {getattr(identity, "entity_uri", None)}
|
|
133
|
+
source_entities.update(getattr(identity, "allowed_source_entities", ()) or ())
|
|
134
|
+
return {
|
|
135
|
+
normalized
|
|
136
|
+
for raw in source_entities
|
|
137
|
+
if (normalized := _normalized_or_none(raw)) is not None
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _normalized_or_none(raw: Any) -> str | None:
|
|
142
|
+
if not isinstance(raw, str):
|
|
143
|
+
return None
|
|
144
|
+
try:
|
|
145
|
+
return normalize_entity_uri(raw)
|
|
146
|
+
except NormalizationError:
|
|
147
|
+
return None
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""Plugin manifest factory for source attestation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from stigmem_node.plugins import PluginManifest
|
|
6
|
+
|
|
7
|
+
from . import handlers
|
|
8
|
+
from .config import SourceAttestationConfig
|
|
9
|
+
|
|
10
|
+
PLUGIN_NAME = "stigmem-plugin-source-attestation"
|
|
11
|
+
PLUGIN_VERSION = "0.1.0"
|
|
12
|
+
REQUIRES_STIGMEM = ">=0.9.0a3"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def plugin_manifest() -> PluginManifest:
|
|
16
|
+
"""Return the entry-point manifest consumed by ``stigmem.plugins`` discovery."""
|
|
17
|
+
|
|
18
|
+
return PluginManifest(
|
|
19
|
+
name=PLUGIN_NAME,
|
|
20
|
+
version=PLUGIN_VERSION,
|
|
21
|
+
requires_stigmem=REQUIRES_STIGMEM,
|
|
22
|
+
capabilities=frozenset(
|
|
23
|
+
{
|
|
24
|
+
"facts.read",
|
|
25
|
+
"facts.write",
|
|
26
|
+
"recall.read",
|
|
27
|
+
"federation.read",
|
|
28
|
+
"federation.write",
|
|
29
|
+
"identity.read",
|
|
30
|
+
"audit.emit",
|
|
31
|
+
"config.read",
|
|
32
|
+
}
|
|
33
|
+
),
|
|
34
|
+
hooks={
|
|
35
|
+
"config_validate": handlers.config_validate,
|
|
36
|
+
"pre_assert_validate": handlers.pre_assert_validate,
|
|
37
|
+
"recall_rank": handlers.recall_rank,
|
|
38
|
+
"federation_inbound_validate": handlers.federation_inbound_validate,
|
|
39
|
+
},
|
|
40
|
+
config_schema=SourceAttestationConfig,
|
|
41
|
+
health_check=handlers.health_check,
|
|
42
|
+
async_safe=True,
|
|
43
|
+
)
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: stigmem-plugin-source-attestation
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Experimental source-attestation plugin for Stigmem.
|
|
5
|
+
Project-URL: Homepage, https://github.com/eidetic-labs/stigmem
|
|
6
|
+
Project-URL: Documentation, https://github.com/eidetic-labs/stigmem/tree/main/features/source-attestation
|
|
7
|
+
Project-URL: Repository, https://github.com/eidetic-labs/stigmem
|
|
8
|
+
Project-URL: Issues, https://github.com/eidetic-labs/stigmem/issues
|
|
9
|
+
Author-email: Eidetic Labs <oss@eidetic-labs.ai>
|
|
10
|
+
License: Apache-2.0
|
|
11
|
+
Keywords: federation,plugins,provenance,source-attestation,stigmem
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Requires-Dist: pydantic<3,>=2
|
|
21
|
+
Requires-Dist: stigmem-node<1.0.0,>=0.9.0a8
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# Stigmem Source Attestation Plugin
|
|
25
|
+
|
|
26
|
+
Experimental source attestation plugin for Stigmem.
|
|
27
|
+
|
|
28
|
+
This package provides the `stigmem-plugin-source-attestation` source package
|
|
29
|
+
for alpha validation. It registers through the `stigmem.plugins` entry point
|
|
30
|
+
group and is loaded by `stigmem-node` only when explicitly installed and
|
|
31
|
+
configured by an operator.
|
|
32
|
+
|
|
33
|
+
## Status
|
|
34
|
+
|
|
35
|
+
Source attestation remains experimental. Installing this package does not add
|
|
36
|
+
assertion-source enforcement, recall source weighting, or federation source
|
|
37
|
+
guards to the supported default surface. Default installs remain inert unless
|
|
38
|
+
the plugin is registered and the operator enables the relevant gates.
|
|
39
|
+
|
|
40
|
+
The package metadata is publication-shaped for the plugin readiness track, but
|
|
41
|
+
registry publication remains on hold until dry-run evidence and maintainer
|
|
42
|
+
clearance are recorded. See the feature record under
|
|
43
|
+
`features/source-attestation/` for the current status, evidence, and security
|
|
44
|
+
notes.
|
|
45
|
+
|
|
46
|
+
## Installation
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
pip install --pre stigmem-node==0.9.0a8 stigmem-plugin-source-attestation==0.1.0
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Enable
|
|
53
|
+
|
|
54
|
+
Set the plugin gate environment variable to opt in:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
export STIGMEM_SOURCE_ATTESTATION_ENABLED=1
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
The default install is inert; source attestation only activates when the
|
|
61
|
+
package is installed, discovered through the `stigmem.plugins` entry point, and
|
|
62
|
+
the operator enables the gate. Enforcement-specific gates such as
|
|
63
|
+
`STIGMEM_SOURCE_ATTESTATION_ENFORCE_ASSERT_VALIDATION` and
|
|
64
|
+
`STIGMEM_SOURCE_ATTESTATION_ENFORCE_FEDERATION_INBOUND` remain opt-in and must
|
|
65
|
+
not be enabled with warn-only mode.
|
|
66
|
+
|
|
67
|
+
## Disable
|
|
68
|
+
|
|
69
|
+
Unset the plugin gate environment variable, or set it to any value other than
|
|
70
|
+
`1`, `true`, `yes`, or `on`:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
unset STIGMEM_SOURCE_ATTESTATION_ENABLED
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The plugin returns to inert state at the next process start. No data migration
|
|
77
|
+
is required; core source, scope, tenant, and audit enforcement continues to hold
|
|
78
|
+
without plugin participation.
|
|
79
|
+
|
|
80
|
+
## Test
|
|
81
|
+
|
|
82
|
+
From a Stigmem repository checkout with development dependencies installed:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
uv run pytest node/tests/plugins/test_source_attestation_plugin_scaffold.py \
|
|
86
|
+
node/tests/plugins/test_source_attestation_plugin_validation.py
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
The package itself ships no separate test tree; upstream plugin validation
|
|
90
|
+
lives in `node/tests/plugins/`.
|
|
91
|
+
|
|
92
|
+
## Uninstall
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
pip uninstall stigmem-plugin-source-attestation
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Removing the package is sufficient. The gate environment variable becomes moot
|
|
99
|
+
once the entry point is no longer discoverable.
|
|
100
|
+
|
|
101
|
+
## Project Links
|
|
102
|
+
|
|
103
|
+
- Repository: <https://github.com/eidetic-labs/stigmem>
|
|
104
|
+
- Feature record: <https://github.com/eidetic-labs/stigmem/tree/main/features/source-attestation>
|
|
105
|
+
- Plugin source: <https://github.com/eidetic-labs/stigmem/tree/main/experimental/source-attestation>
|
|
106
|
+
- Issue tracker: <https://github.com/eidetic-labs/stigmem/issues>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
stigmem_plugin_source_attestation/__init__.py,sha256=YBQ6ZoUBCzantFo-SdzDz0kj-4h_ARC26Y2PPojmdhI,275
|
|
2
|
+
stigmem_plugin_source_attestation/config.py,sha256=Uk8LyNig3hMDdDd1S20zZE6AtrCitEkAh_CddNVW7ig,1743
|
|
3
|
+
stigmem_plugin_source_attestation/handlers.py,sha256=DodZgnBoMpyeKTwlM4uHnpOgJ7lPEnANHpjAML8oMYM,5283
|
|
4
|
+
stigmem_plugin_source_attestation/manifest.py,sha256=Ean8DS_9_9gXjfCCOsSGSrfiM2ifJLmmSlUw9NTilR4,1300
|
|
5
|
+
stigmem_plugin_source_attestation-0.1.0.dist-info/METADATA,sha256=M3y-fOgetcnomA88m9EsTxWJrEDyBxnrnjcB-h0OdRE,3923
|
|
6
|
+
stigmem_plugin_source_attestation-0.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
7
|
+
stigmem_plugin_source_attestation-0.1.0.dist-info/entry_points.txt,sha256=OUSYxVhSNsIbv3UNoWu9FVmhJEmhZYx6N-Zkefq25V0,89
|
|
8
|
+
stigmem_plugin_source_attestation-0.1.0.dist-info/RECORD,,
|